package com.boardgamegeek.ui.adapter; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.os.AsyncTask; import android.support.annotation.NonNull; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Filter; import android.widget.Filterable; import android.widget.TextView; import com.boardgamegeek.R; import com.boardgamegeek.provider.BggContract.Buddies; import com.boardgamegeek.provider.BggContract.PlayPlayers; import com.boardgamegeek.provider.BggContract.Plays; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.concurrent.ExecutionException; import timber.log.Timber; public class BuddyNameAdapter extends ArrayAdapter<BuddyNameAdapter.Result> implements Filterable { public static class Result { private final String username; public Result(String username) { this.username = username; } @Override public String toString() { return username; } } private static final ArrayList<Result> EMPTY_LIST = new ArrayList<>(); private final ContentResolver resolver; private final LayoutInflater inflater; private final ArrayList<Result> resultList = new ArrayList<>(); public BuddyNameAdapter(Context context) { super(context, R.layout.autocomplete_item, EMPTY_LIST); resolver = context.getContentResolver(); inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public int getCount() { return resultList.size(); } @Override public Result getItem(int index) { if (index < resultList.size()) { return resultList.get(index); } else { return null; } } @NonNull @Override public View getView(final int position, final View convertView, @NonNull final ViewGroup parent) { View view = convertView; if (view == null) { view = inflater.inflate(R.layout.autocomplete_item, parent, false); } final Result result = getItem(position); if (result == null) { return view; } TextView titleView = (TextView) view.findViewById(R.id.autocomplete_item); if (titleView != null) { if (result.username == null) { titleView.setVisibility(View.GONE); } else { titleView.setVisibility(View.VISIBLE); titleView.setText(result.username); } } return view; } @NonNull @Override public Filter getFilter() { return new PlayerFilter(); } public class PlayerFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence constraint) { final String filter = constraint == null ? "" : constraint.toString(); if (filter.length() == 0) { return null; } AsyncTask<Void, Void, List<Result>> playerQueryTask = new AsyncTask<Void, Void, List<Result>>() { @Override protected List<Result> doInBackground(Void... params) { return queryPlayerHistory(resolver, filter); } }.execute(); HashSet<String> buddyUsernames = new HashSet<>(); List<Result> buddies = queryBuddies(resolver, filter, buddyUsernames); ArrayList<Result> resultList = new ArrayList<>(); if (buddies != null) { resultList.addAll(buddies); } try { List<Result> players = playerQueryTask.get(); for (Result player : players) { if (TextUtils.isEmpty(player.username) || !buddyUsernames.contains(player.username)) resultList.add(player); } } catch (ExecutionException | InterruptedException e) { Timber.e(e, "Failed waiting for player query results."); } final FilterResults filterResults = new FilterResults(); filterResults.values = resultList; filterResults.count = resultList.size(); return filterResults; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { resultList.clear(); if (results != null && results.count > 0) { resultList.addAll((ArrayList<Result>) results.values); notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } private static final String PLAYER_SELECTION = PlayPlayers.USER_NAME + " LIKE ?"; private static final String[] PLAYER_PROJECTION = new String[] { PlayPlayers._ID, PlayPlayers.USER_NAME }; private static final int PLAYER_USERNAME = 1; private static List<Result> queryPlayerHistory(ContentResolver resolver, String input) { String where = null; String[] whereArgs = null; if (!TextUtils.isEmpty(input)) { where = PLAYER_SELECTION; String param = input + "%"; whereArgs = new String[] { param }; } Cursor cursor = resolver.query(Plays.buildPlayersByUniqueUserUri(), PLAYER_PROJECTION, where, whereArgs, PlayPlayers.NAME); try { List<Result> results = new ArrayList<>(); if (cursor != null) { cursor.moveToPosition(-1); while (cursor.moveToNext()) { String username = cursor.getString(PLAYER_USERNAME); results.add(new Result(username)); } } return results; } finally { if (cursor != null) { cursor.close(); } } } private static final String BUDDY_SELECTION = Buddies.BUDDY_NAME + " LIKE ?"; private static final String[] BUDDY_PROJECTION = { Buddies._ID, Buddies.BUDDY_NAME }; private static final int BUDDY_NAME = 1; private static List<Result> queryBuddies(ContentResolver resolver, String input, HashSet<String> usernames) { String where = null; String[] whereArgs = null; if (!TextUtils.isEmpty(input)) { where = BUDDY_SELECTION; String param = input + "%"; whereArgs = new String[] { param }; } Cursor cursor = resolver.query(Buddies.CONTENT_URI, BUDDY_PROJECTION, where, whereArgs, Buddies.NAME_SORT); try { List<Result> results = new ArrayList<>(); if (cursor != null) { cursor.moveToPosition(-1); while (cursor.moveToNext()) { String userName = cursor.getString(BUDDY_NAME); results.add(new Result(userName)); usernames.add(userName); } } return results; } finally { if (cursor != null) { cursor.close(); } } } }