Changes the autocomplete list to show the username, display name, and avatar of each suggestion for @ mentions.

This commit is contained in:
Vavassor 2017-06-18 23:34:48 -04:00
parent 74aa866647
commit fcdb507ef0
2 changed files with 106 additions and 15 deletions

View File

@ -42,6 +42,7 @@ import android.provider.MediaStore;
import android.support.annotation.AttrRes; import android.support.annotation.AttrRes;
import android.support.annotation.LayoutRes; import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v13.view.inputmethod.InputConnectionCompat; import android.support.v13.view.inputmethod.InputConnectionCompat;
@ -59,8 +60,10 @@ import android.text.TextUtils;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.text.style.URLSpan; import android.text.style.URLSpan;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
@ -86,6 +89,7 @@ import com.keylesspalace.tusky.util.ParserUtils;
import com.keylesspalace.tusky.util.SpanUtils; import com.keylesspalace.tusky.util.SpanUtils;
import com.keylesspalace.tusky.util.ThemeUtils; import com.keylesspalace.tusky.util.ThemeUtils;
import com.keylesspalace.tusky.view.EditTextTyped; import com.keylesspalace.tusky.view.EditTextTyped;
import com.keylesspalace.tusky.view.RoundedTransformation;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target; import com.squareup.picasso.Target;
@ -320,8 +324,7 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
} }
}); });
textEditor.setAdapter(new MentionAutoCompleteAdapter(this, textEditor.setAdapter(new MentionAutoCompleteAdapter(this, R.layout.item_autocomplete));
android.R.layout.simple_dropdown_item_1line));
textEditor.setTokenizer(new MentionTokenizer()); textEditor.setTokenizer(new MentionTokenizer());
// Add any mentions to the text field when a reply is first composed. // Add any mentions to the text field when a reply is first composed.
@ -1247,20 +1250,16 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
} }
/** /**
* Does a synchronous search request for usernames fulfilling the given partial mention text. * Does a synchronous search request for accounts fulfilling the given partial mention text.
*/ */
private ArrayList<String> autocompleteMention(String mention) { private ArrayList<Account> autocompleteMention(String mention) {
ArrayList<String> resultList = new ArrayList<>(); ArrayList<Account> resultList = new ArrayList<>();
try { try {
List<Account> accountList = mastodonAPI.searchAccounts(mention, false, 5) List<Account> accountList = mastodonAPI.searchAccounts(mention, false, 40)
.execute() .execute()
.body(); .body();
/* Match only accounts whose username contains the partial mention text, because if (accountList != null) {
searches also return matches for display names, which aren't relevant here. */ resultList.addAll(accountList);
for (Account account : accountList) {
if (account.username.toLowerCase().contains(mention.toLowerCase())) {
resultList.add(account.username);
}
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, String.format("Autocomplete search for %s failed.", mention)); Log.e(TAG, String.format("Autocomplete search for %s failed.", mention));
@ -1344,11 +1343,13 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
} }
} }
private class MentionAutoCompleteAdapter extends ArrayAdapter<String> implements Filterable { private class MentionAutoCompleteAdapter extends ArrayAdapter<Account> implements Filterable {
private ArrayList<String> resultList; private ArrayList<Account> resultList;
private @LayoutRes int layoutId;
MentionAutoCompleteAdapter(Context context, @LayoutRes int resource) { MentionAutoCompleteAdapter(Context context, @LayoutRes int resource) {
super(context, resource); super(context, resource);
layoutId = resource;
} }
@Override @Override
@ -1357,13 +1358,18 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
} }
@Override @Override
public String getItem(int index) { public Account getItem(int index) {
return resultList.get(index); return resultList.get(index);
} }
@Override @NonNull @Override @NonNull
public Filter getFilter() { public Filter getFilter() {
return new Filter() { return new Filter() {
@Override
public CharSequence convertResultToString(Object resultValue) {
return ((Account) resultValue).username;
}
@Override @Override
protected FilterResults performFiltering(CharSequence constraint) { protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults(); FilterResults filterResults = new FilterResults();
@ -1385,5 +1391,40 @@ public class ComposeActivity extends BaseActivity implements ComposeOptionsFragm
} }
}; };
} }
@Override @NonNull
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view = convertView;
Context context = getContext();
if (convertView == null) {
LayoutInflater layoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(layoutId, null);
}
Account account = getItem(position);
if (account != null) {
TextView username = (TextView) view.findViewById(R.id.username);
TextView displayName = (TextView) view.findViewById(R.id.display_name);
ImageView avatar = (ImageView) view.findViewById(R.id.avatar);
String format = getContext().getString(R.string.status_username_format);
String formattedUsername = String.format(format, account.username);
username.setText(formattedUsername);
displayName.setText(account.getDisplayName());
if (!account.avatar.isEmpty()) {
Picasso.with(context)
.load(account.avatar)
.placeholder(R.drawable.avatar_default)
.error(R.drawable.avatar_error)
.transform(new RoundedTransformation(7, 0))
.into(avatar);
}
}
return view;
}
} }
} }

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:gravity="center_vertical">
<ImageView
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:id="@+id/avatar"
android:src="@drawable/avatar_default"
android:contentDescription="@null" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_toRightOf="@id/avatar"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/display_name"
android:maxLines="1"
android:ellipsize="end"
android:textSize="16sp"
android:textColor="?android:textColorPrimary"
android:textStyle="normal|bold" />
<Space
android:layout_width="match_parent"
android:layout_height="4dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/username"
android:maxLines="1"
android:ellipsize="end"
android:textSize="14sp"
android:textColor="?android:textColorSecondary" />
</LinearLayout>
</RelativeLayout>