Index: bp-core/bp-core-classes.php
--- bp-core/bp-core-classes.php
+++ bp-core/bp-core-classes.php
@@ -1,8 +1,399 @@
 <?php
+
 // Exit if accessed directly
 if ( !defined( 'ABSPATH' ) ) exit;
 
 /**
+ * BuddyPress User Query class
+ *
+ * Used for querying users in a BuddyPress context, in situations where more
+ * than usermeta is needed. (Friends, Activity, etc...)
+ *
+ * @since BuddyPress (1.7)
+ */
+class BP_User_Query {
+
+	/** Variables *************************************************************/
+
+	/**
+	 * Array of variables to query with
+	 *
+	 * @since BuddyPress (1.7)
+	 * @var array
+	 */
+	public $query_vars = array();
+
+	/**
+	 * List of found users and their respective data
+	 *
+	 * @since BuddyPress (1.7)
+	 * @access public To allow components to manipulate them
+	 * @var array
+	 */
+	public $results = array();
+
+	/**
+	 * Total number of found users for the current query
+	 *
+	 * @since BuddyPress (1.7)
+	 * @access public To allow components to manipulate it
+	 * @var int
+	 */
+	public $total_users = 0;
+
+	/**
+	 * List of found user ID's
+	 *
+	 * @since BuddyPress (1.7)
+	 * @access private To disallow components from manipulating them
+	 * @var array
+	 */
+	private $user_ids = array();
+
+	/**
+	 * SQL clauses for the user ID query
+	 *
+	 * @since BuddyPress (1.7)
+	 * @access protected To disallow
+	 * @var array()
+	 */
+	private $uid_clauses = array();
+
+	/**
+	 * SQL database column name to order by
+	 *
+	 * @since BuddyPress (1.7)
+	 * @var string
+	 */
+	private $uid_name = '';
+
+	/** Methods ***************************************************************/
+
+	/**
+	 * Constructor
+	 *
+	 * @since 1.7
+	 *
+	 * @param string|array $query The query variables
+	 */
+	public function __construct( $query = null ) {
+		if ( ! empty( $query ) ) {
+			$this->query_vars = wp_parse_args( $query, array(
+				'type'            => 'newest',
+				'per_page'        => 0,
+				'page'            => 1,
+				'user_id'         => 0,
+				'include'         => false,
+				'search_terms'    => false,
+				'exclude'         => false,
+				'meta_key'        => false,
+				'meta_value'      => false,
+				'populate_extras' => true,
+				'count_total'     => 'count_query'
+			) );
+
+			// Get user ids
+			$this->prepare_user_ids_query();
+			$this->do_user_ids_query();
+		}
+
+		// Bail if no user IDs were found
+		if ( empty( $this->user_ids ) ) {
+			return;
+		}
+
+		// Fetch additional data. First, using WP_User_Query
+		$this->do_wp_user_query();
+
+		// Get BuddyPress specific user data
+		if ( !empty( $this->query_vars['populate_extras'] ) ) {
+			$this->populate_extras();
+		}
+	}
+
+	/**
+	 * Prepare the query for user_ids
+	 *
+	 * @since BuddyPress (1.7)
+	 */
+	protected function prepare_user_ids_query() {
+		global $wpdb, $bp;
+
+		// Default query variables used here
+		$type         = '';
+		$per_page     = 0;
+		$page         = 1;
+		$user_id      = 0;
+		$include      = false;
+		$search_terms = false;
+		$exclude      = false;
+		$meta_key     = false;
+		$meta_value   = false;
+
+		extract( $this->query_vars );
+
+		// Setup the main SQL query container
+		$sql = array(
+			'select'  => '',
+			'where'   => array(),
+			'orderby' => '',
+			'order'   => '',
+			'limit'   => ''
+		);
+
+		/** TYPE **************************************************************/
+
+		// Determines the sort order, which means it also determines where the
+		// user IDs are drawn from (the SELECT and WHERE statements)
+		if ( in_array( $type, array( 'active', 'online', 'newest', 'popular' ) ) ) {
+			$this->uid_name = 'user_id';
+			$sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$wpdb->usermeta} u";
+			$sql['where'][] = "u.meta_key = 'last_activity'";
+
+			if ( 'newest' == $type ) {
+				$sql['orderby'] = "ORDER BY u.user_id";
+			} else {
+				$sql['orderby'] = "ORDER BY u.meta_value";
+			}
+
+			$sql['order'] = "DESC";
+
+		// Order by username, which gets tricky when xprofile is activated
+		} elseif ( 'alphabetical' == $type ) {
+
+			// We prefer to do alphabetical sorts against the display_name field
+			// of wp_users, because the table is smaller and better indexed. We
+			// can do so if xprofile sync is enabled, or if xprofile is inactive.
+			//
+			// @todo remove need for bp_is_active() check
+			if ( ! bp_disable_profile_sync() || ! bp_is_active( 'xprofile' ) ) {
+				$this->uid_name = 'ID';
+				$sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$wpdb->users} u";
+				$sql['orderby'] = "ORDER BY u.display_name";
+				$sql['order']   = "ASC";
+
+			// When profile sync is disabled, alphabetical sorts must happen against
+			// the xprofile table
+			} else {
+				$fullname_field_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s", bp_xprofile_fullname_field_name() ) );
+
+				$this->uid_name = 'user_id';
+				$sql['select']  = "SELECT DISTINCT u.{$this->uid_name} as id FROM {$bp->profile->table_name_data} u";
+				$sql['where'][] = "u.field_id = {$fullname_field_id}";
+				$sql['orderby'] = "ORDER BY u.value";
+				$sql['order']   = "ASC";
+			}
+		}
+
+		/** WHERE *************************************************************/
+
+		// 'include' - User ids to include in the results
+		if ( false !== $include ) {
+			$include        = wp_parse_id_list( $include );
+			$include_ids    = $wpdb->escape( implode( ',', (array) $include ) );
+			$sql['where'][] = "u.{$this->uid_name} IN ({$include_ids})";
+		}
+
+		// 'exclude' - User ids to exclude from the results
+		if ( false !== $exclude ) {
+			$exclude        = wp_parse_id_list( $exclude );
+			$exclude_ids    = $wpdb->escape( implode( ',', (array) $exclude ) );
+			$sql['where'][] = "u.{$this->uid_name} NOT IN ({$exclude_ids})";
+		}
+
+		// 'user_id' - When a user id is passed, limit to the friends of the user
+		// @todo remove need for bp_is_active() check
+		if ( !empty( $user_id ) && bp_is_active( 'friends' ) ) {
+			$friend_ids = friends_get_friend_user_ids( $user_id );
+			$friend_ids = $wpdb->escape( implode( ',', (array) $friend_ids ) );
+
+			if ( !empty( $friend_ids ) ) {
+				$sql['where'][] = "u.{$this->uid_name} NOT IN ({$friend_ids})";
+			} else {
+				// If the user has no friends, make sure the query returns null
+				$sql['where'][] = "0 = 1";
+			}
+		}
+
+		/** Search Terms ******************************************************/
+
+		// To avoid global joins, do a separate query
+		// @todo remove need for bp_is_active() check
+		if ( false !== $search_terms && bp_is_active( 'xprofile' ) ) {
+			$found_user_ids = $wpdb->get_row( $wpdb->prepare( "SELECT user_id FROM {$bp->profile->table_name_data} WHERE value LIKE %s", '%%' . like_escape( $search_terms ) . '%%' ), ARRAY_N );
+
+			if ( ! empty( $found_user_ids ) ) {
+				$sql['where'][] = "u.{$this->uid_name} IN (" . implode( ',', wp_parse_id_list( $found_user_ids ) ) . ")";
+			}
+		}
+
+		// 'meta_key', 'meta_value' - join against usermeta
+		// @todo Use WP_User_Query to get IDs for an IN clause?
+		if ( false !== $meta_key ) {
+			$sql['select']  .= " LEFT JOIN {$wpdb->usermeta} um ON (um.user_id = u.{$this->uid_name})";
+			$sql['where'][]  = $wpdb->prepare( "um.meta_key = %s", $meta_key );
+
+			// If a meta value is provided, match it
+			if ( false !== $meta_value ) {
+				$sql['where'][] = $wpdb->prepare( "um.meta_value = %s", $meta_value );
+			}
+		}
+
+		// 'per_page', 'page' - handles LIMIT
+		if ( !empty( $per_page ) && !empty( $page ) ) {
+			$sql['limit'] = $wpdb->prepare( "LIMIT %d, %d", intval( ( $page - 1 ) * $per_page ), intval( $per_page ) );
+		} else {
+			$sql['limit'] = '';
+		}
+
+		// Assemble the query chunks
+		$this->uid_clauses['select']  = $sql['select'];
+		$this->uid_clauses['where']   = ! empty( $sql['where'] ) ? 'WHERE ' . implode( ' AND ', $sql['where'] ) : '';
+		$this->uid_clauses['orderby'] = $sql['orderby'];
+		$this->uid_clauses['order']   = $sql['order'];
+		$this->uid_clauses['limit']   = $sql['limit'];
+
+		do_action_ref_array( 'bp_pre_user_query', array( &$this ) );
+	}
+
+	/**
+	 * Perform a database query to specifically get only user IDs, using
+	 * existing query variables set previously in the constructor.
+	 *
+	 * Also used to quickly perform user total counts.
+	 *
+	 * @since BuddyPress (1.7)
+	 */
+	protected function do_user_ids_query() {
+		global $wpdb;
+
+		// If counting using SQL_CALC_FOUND_ROWS, set it up here
+		if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
+			$this->uid_clauses['select'] = str_replace( 'SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $this->uid_clauses['select'] );
+		}
+
+		// Get the specific user ids
+		$this->user_ids = $wpdb->get_col( $wpdb->prepare( "{$this->uid_clauses['select']} {$this->uid_clauses['where']} {$this->uid_clauses['orderby']} {$this->uid_clauses['order']} {$this->uid_clauses['limit']}" ) );
+
+		// Get the total user count
+		if ( 'sql_calc_found_rows' == $this->query_vars['count_total'] ) {
+			$this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "SELECT FOUND_ROWS()", $this ) );
+		} elseif ( 'count_query' == $this->query_vars['count_total'] ) {
+			$count_select      = preg_replace( '/^SELECT.*?FROM (\S+) u/', "SELECT COUNT(DISTINCT u.{$this->uid_name}) FROM $1 u", $this->uid_clauses['select'] );
+			$this->total_users = $wpdb->get_var( apply_filters( 'bp_found_user_query', "{$count_select} {$this->uid_clauses['where']}", $this ) );
+		}
+	}
+
+	/**
+	 * Perform a database query using the WP_User_Query() object, using existing
+	 * fields, variables, and user ID's set previously in this class.
+	 *
+	 * @since BuddyPress (1.7)
+	 */
+	protected function do_wp_user_query() {
+		$wp_user_query = new WP_User_Query( apply_filters( 'bp_wp_user_query_args', array(
+
+			// Relevant
+			'fields'      => array( 'ID', 'user_registered', 'user_login', 'user_nicename', 'display_name', 'user_email' ),
+			'include'     => $this->user_ids,
+
+			// Overrides
+			'blog_id'     => 0,    // BP does not require blog roles
+			'count_total' => false // We already have a count
+
+		), $this ) );
+
+		// Reindex for easier matching
+		$r = array();
+		foreach ( $wp_user_query->results as $u ) {
+			$r[ $u->ID ] = $u;
+		}
+
+		// Match up to the user ids from the main query
+		foreach ( $this->user_ids as $uid ) {
+			if ( isset( $r[ $uid ] ) ) {
+				$this->results[ $uid ] = $r[ $uid ];
+
+				// The BP template functions expect an 'id'
+				// (as opposed to 'ID') property
+				$this->results[ $uid ]->id = $uid;
+			}
+		}
+	}
+
+	/**
+	 * Perform a database query to populate any extra metadata we might need.
+	 * Different components will hook into the 'bp_user_query_populate_extras'
+	 * action to loop in the things they want.
+	 *
+	 * @since BuddyPress (1.7)
+	 *
+	 * @global BuddyPress $bp
+	 * @global WPDB $wpdb
+	 * @return
+	 */
+	protected function populate_extras() {
+		global $wpdb;
+
+		// Bail if no users
+		if ( empty( $this->user_ids ) || empty( $this->results ) )
+			return;
+
+		// Turn user ID's into a query-usable, comma separated value
+		$user_ids_sql = implode( ',', wp_parse_id_list( $this->user_ids ) );
+
+		/**
+		 * Use this action to independently populate your own custom extras.
+		 *
+		 * Note that anything you add here should query using $user_ids_sql, to
+		 * avoid running multiple queries per user in the loop.
+		 *
+		 * Two BuddyPress components currently do this:
+		 * - XProfile: To override display names
+		 * - Friends:  To set whether or not a user is the current users friend
+		 *
+		 * @see bp_xprofile_filter_user_query_populate_extras()
+		 * @see bp_friends_filter_user_query_populate_extras()
+		 */
+		do_action_ref_array( 'bp_user_query_populate_extras', array( $this, $user_ids_sql ) );
+
+		// Fetch usermeta data
+		// We want the three following pieces of info from usermeta:
+		// - friend count
+		// - last activity
+		// - latest update
+		$total_friend_count_key = bp_get_user_meta_key( 'total_friend_count' );
+		$last_activity_key      = bp_get_user_meta_key( 'last_activity'      );
+		$bp_latest_update_key   = bp_get_user_meta_key( 'bp_latest_update'   );
+
+		// Create, prepare, and run the seperate usermeta query
+		$user_metas = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, meta_key, meta_value FROM {$wpdb->usermeta} WHERE meta_key IN (%s,%s,%s) AND user_id IN ({$user_ids_sql})", $total_friend_count_key, $last_activity_key, $bp_latest_update_key ) );
+
+		// The $members_template global expects the index key to be different
+		// from the meta_key in some cases, so we rejig things here.
+		foreach ( $user_metas as $user_meta ) {
+			switch ( $user_meta->meta_key ) {
+				case $total_friend_count_key :
+					$key = 'total_friend_count';
+					break;
+
+				case $last_activity_key :
+					$key = 'last_activity';
+					break;
+
+				case $bp_latest_update_key :
+					$key = 'latest_update';
+					break;
+			}
+
+			if ( isset( $this->results[ $user_meta->user_id ] ) ) {
+				$this->results[ $user_meta->user_id ]->{$key} = $user_meta->meta_value;
+			}
+		}
+	}
+}
+
+/**
  * BP_Core_User class can be used by any component. It will fetch useful
  * details for any user when provided with a user_id.
  *
@@ -201,6 +592,8 @@
 	function get_users( $type, $limit = 0, $page = 1, $user_id = 0, $include = false, $search_terms = false, $populate_extras = true, $exclude = false, $meta_key = false, $meta_value = false ) {
 		global $wpdb, $bp;
 
+		_deprecated_function( __METHOD__, '1.7', 'BP_User_Query' );
+
 		$sql = array();
 
 		$sql['select_main'] = "SELECT DISTINCT u.ID as id, u.user_registered, u.user_nicename, u.user_login, u.display_name, u.user_email";
Index: bp-friends/bp-friends-filters.php
--- bp-friends/bp-friends-filters.php No Base Revision
+++ bp-friends/bp-friends-filters.php Locally New
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * BuddyPress Friend Filters
+ *
+ * @package BuddyPress
+ * @subpackage FriendsFilters
+ */
+
+/**
+ * Filter BP_User_Query::populate_extras to override each queries users fullname
+ *
+ * @since BuddyPress (1.7)
+ *
+ * @global BuddyPress $bp
+ * @global WPDB $wpdb
+ * @param BP_User_Query $user_query
+ * @param string $user_ids_sql
+ */
+function bp_friends_filter_user_query_populate_extras( BP_User_Query $user_query, $user_ids_sql ) {
+	global $bp, $wpdb;
+
+	// Fetch whether or not the user is a friend of the current user
+	$friend_status = $wpdb->get_results( $wpdb->prepare( "SELECT initiator_user_id, friend_user_id, is_confirmed FROM {$bp->friends->table_name} WHERE (initiator_user_id = %d AND friend_user_id IN ( {$user_ids_sql} ) ) OR (initiator_user_id IN ( {$user_ids_sql} ) AND friend_user_id = %d )", bp_loggedin_user_id(), bp_loggedin_user_id() ) );
+
+	// The "friend" is the user ID in the pair who is *not* the logged in user
+	foreach ( $friend_status as $fs ) {
+		$friend_id = bp_loggedin_user_id() == $fs->initiator_user_id ? $fs->friend_user_id : $fs->initiator_user_id;
+
+		if ( isset( $user_query->results[ $friend_id ] ) ) {
+			$user_query->results[ $friend_id ]->is_friend = $fs->is_confirmed;
+		}
+	}
+}
+add_filter( 'bp_user_query_populate_extras', 'bp_friends_filter_user_query_populate_extras', 4, 2 );
Index: bp-members/bp-members-functions.php
--- bp-members/bp-members-functions.php
+++ bp-members/bp-members-functions.php
@@ -71,28 +71,48 @@
 /**
  * Return an array of users IDs based on the parameters passed.
  *
+ * Since BuddyPress 1.7, bp_core_get_users() uses BP_User_Query. If you
+ * need backward compatibility with BP_Core_User::get_users(), filter the
+ * bp_use_legacy_user_query value, returning true.
+ *
  * @package BuddyPress Core
  */
 function bp_core_get_users( $args = '' ) {
 
-	$defaults = array(
-		'type'            => 'active', // active, newest, alphabetical, random or popular
-		'user_id'         => false,    // Pass a user_id to limit to only friend connections for this user
-		'exclude'         => false,    // Users to exclude from results
-		'search_terms'    => false,    // Limit to users that match these search terms
-		'meta_key'        => false,    // Limit to users who have this piece of usermeta
-		'meta_value'      => false,    // With meta_key, limit to users where usermeta matches this value
+	// Parse the user query arguments
+	$params = wp_parse_args( $args, array(
+		'type'            => 'active',     // active, newest, alphabetical, random or popular
+		'user_id'         => false,        // Pass a user_id to limit to only friend connections for this user
+		'exclude'         => false,        // Users to exclude from results
+		'search_terms'    => false,        // Limit to users that match these search terms
+		'meta_key'        => false,        // Limit to users who have this piece of usermeta
+		'meta_value'      => false,        // With meta_key, limit to users where usermeta matches this value
+		'include'         => false,        // Pass comma separated list of user_ids to limit to only these users
+		'per_page'        => 20,           // The number of results to return per page
+		'page'            => 1,            // The page to return if limiting per page
+		'populate_extras' => true,         // Fetch the last active, where the user is a friend, total friend count, latest update
+		'count_total'     => 'count_query' // What kind of total user count to do, if any. 'count_query', 'sql_calc_found_rows', or false
+	) );
 
-		'include'         => false,    // Pass comma separated list of user_ids to limit to only these users
-		'per_page'        => 20,       // The number of results to return per page
-		'page'            => 1,        // The page to return if limiting per page
-		'populate_extras' => true,     // Fetch the last active, where the user is a friend, total friend count, latest update
-	);
+	// For legacy users. Use of BP_Core_User::get_users() is deprecated.
+	if ( apply_filters( 'bp_use_legacy_user_query', false, __FUNCTION__, $params ) ) {
+		extract( $params, EXTR_SKIP );
+		$retval = BP_Core_User::get_users( $type, $per_page, $page, $user_id, $include, $search_terms, $populate_extras, $exclude, $meta_key, $meta_value );
 
-	$params = wp_parse_args( $args, $defaults );
-	extract( $params, EXTR_SKIP );
+	// Default behavior as of BuddyPress 1.7
+	} else {		
 
-	return apply_filters( 'bp_core_get_users', BP_Core_User::get_users( $type, $per_page, $page, $user_id, $include, $search_terms, $populate_extras, $exclude, $meta_key, $meta_value ), $params );
+		// Get users like we were asked to do...
+		$users = new BP_User_Query( $params );
+
+		// ...but reformat the results to match bp_core_get_users() behavior.
+		$retval = array(
+			'users' => array_values( $users->results ),
+			'total' => $users->total_users
+		);
+	}
+
+	return apply_filters( 'bp_core_get_users', $retval, $params );
 }
 
 /**
Index: bp-xprofile/bp-xprofile-filters.php
--- bp-xprofile/bp-xprofile-filters.php
+++ bp-xprofile/bp-xprofile-filters.php
@@ -219,6 +219,31 @@
 }
 add_filter( 'comments_array', 'xprofile_filter_comments', 10, 2 );
 
+/**
+ * Filter BP_User_Query::populate_extras to override each queries users fullname
+ *
+ * @since BuddyPress (1.7)
+ *
+ * @global BuddyPress $bp
+ * @global WPDB $wpdb
+ * @param BP_User_Query $user_query
+ * @param string $user_ids_sql
+ */
+function bp_xprofile_filter_user_query_populate_extras( BP_User_Query $user_query, $user_ids_sql ) {
+	global $bp, $wpdb;
 
+	if ( bp_is_active( 'xprofile' ) && ( 'alphabetical' != $user_query->query_vars['type'] ) ) {
+		$fullname_field_id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM {$bp->profile->table_name_fields} WHERE name = %s", bp_xprofile_fullname_field_name() ) );
+		$user_id_names     = $wpdb->get_results( $wpdb->prepare( "SELECT user_id, value as fullname FROM {$bp->profile->table_name_data} WHERE user_id IN ({$user_ids_sql}) AND field_id = {$fullname_field_id}" ) );
 
+		// Loop through names and override each user's fullname
+		foreach ( $user_id_names as $user ) {
+			if ( isset( $user_query->results[ $user->user_id ] ) ) {
+				$user_query->results[ $user->user_id ]->fullname = $user->fullname;
+			}
+		}
+	}
+}
+add_filter( 'bp_user_query_populate_extras', 'bp_xprofile_filter_user_query_populate_extras', 2, 2 );
+
 ?>
