Search now actually searches, yay

This commit is contained in:
Grishka
2022-03-31 17:49:54 +03:00
parent fa9112e117
commit c60bc253e5
27 changed files with 1046 additions and 122 deletions

View File

@@ -17,7 +17,6 @@ import org.joinmastodon.android.api.requests.search.GetSearchResults;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.EmojiCategory;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.SearchResults;
import org.joinmastodon.android.ui.drawables.ComposeAutocompleteBackgroundDrawable;
@@ -27,13 +26,10 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.Collections;
import java.util.List;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.api.APIRequest;
@@ -165,7 +161,7 @@ public class ComposeAutocompleteViewController{
.filter(e->e.visibleInPicker && e.shortcode.startsWith(_text))
.map(WrappedEmoji::new)
.collect(Collectors.toList());
updateList(oldList, emojis, emojisAdapter, (e1, e2)->e1.emoji.shortcode.equals(e2.emoji.shortcode));
UiUtils.updateList(oldList, emojis, list, emojisAdapter, (e1, e2)->e1.emoji.shortcode.equals(e2.emoji.shortcode));
}
}
@@ -181,39 +177,15 @@ public class ComposeAutocompleteViewController{
return contentView;
}
private <T> void updateList(List<T> oldList, List<T> newList, RecyclerView.Adapter<?> adapter, BiPredicate<T, T> areItemsSame){
DiffUtil.calculateDiff(new DiffUtil.Callback(){
@Override
public int getOldListSize(){
return oldList.size();
}
@Override
public int getNewListSize(){
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition){
return areItemsSame.test(oldList.get(oldItemPosition), newList.get(newItemPosition));
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition){
return true;
}
}).dispatchUpdatesTo(adapter);
}
private void doSearchUsers(){
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.ACCOUNTS)
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.ACCOUNTS, false)
.setCallback(new Callback<>(){
@Override
public void onSuccess(SearchResults result){
currentRequest=null;
List<WrappedAccount> oldList=users;
users=result.accounts.stream().map(WrappedAccount::new).collect(Collectors.toList());
updateList(oldList, users, usersAdapter, (a1, a2)->a1.account.id.equals(a2.account.id));
UiUtils.updateList(oldList, users, list, usersAdapter, (a1, a2)->a1.account.id.equals(a2.account.id));
if(listIsHidden){
listIsHidden=false;
V.setVisibilityAnimated(list, View.VISIBLE);
@@ -230,14 +202,14 @@ public class ComposeAutocompleteViewController{
}
private void doSearchHashtags(){
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.HASHTAGS)
currentRequest=new GetSearchResults(lastText, GetSearchResults.Type.HASHTAGS, false)
.setCallback(new Callback<>(){
@Override
public void onSuccess(SearchResults result){
currentRequest=null;
List<Hashtag> oldList=hashtags;
hashtags=result.hashtags;
updateList(oldList, hashtags, hashtagsAdapter, (t1, t2)->t1.name.equals(t2.name));
UiUtils.updateList(oldList, hashtags, list, hashtagsAdapter, (t1, t2)->t1.name.equals(t2.name));
if(listIsHidden){
listIsHidden=false;
V.setVisibilityAnimated(list, View.VISIBLE);

View File

@@ -0,0 +1,92 @@
package org.joinmastodon.android.ui.displayitems;
import android.content.Context;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class AccountStatusDisplayItem extends StatusDisplayItem{
public final Account account;
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
private CharSequence parsedName;
public ImageLoaderRequest avaRequest;
public AccountStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Account account){
super(parentID, parentFragment);
this.account=account;
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
emojiHelper.setText(parsedName);
if(!TextUtils.isEmpty(account.avatar))
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
}
@Override
public Type getType(){
return Type.ACCOUNT;
}
@Override
public int getImageCount(){
return 1+emojiHelper.getImageCount();
}
@Override
public ImageLoaderRequest getImageRequest(int index){
if(index==0)
return avaRequest;
return emojiHelper.getImageRequest(index-1);
}
public static class Holder extends StatusDisplayItem.Holder<AccountStatusDisplayItem> implements ImageLoaderViewHolder{
private final TextView name, username;
private final ImageView photo;
public Holder(Context context, ViewGroup parent){
super(context, R.layout.display_item_account, parent);
name=findViewById(R.id.name);
username=findViewById(R.id.username);
photo=findViewById(R.id.photo);
photo.setOutlineProvider(OutlineProviders.roundedRect(12));
photo.setClipToOutline(true);
}
@Override
public void onBind(AccountStatusDisplayItem item){
name.setText(item.parsedName);
username.setText("@"+item.account.acct);
}
@Override
public void setImage(int index, Drawable image){
if(image instanceof Animatable && !((Animatable) image).isRunning())
((Animatable) image).start();
if(index==0){
photo.setImageDrawable(image);
}else{
item.emojiHelper.setImageDrawable(index-1, image);
name.invalidate();
}
}
@Override
public void clearImage(int index){
setImage(index, null);
}
}
}

View File

@@ -0,0 +1,48 @@
package org.joinmastodon.android.ui.displayitems;
import android.content.Context;
import android.view.ViewGroup;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.ui.views.HashtagChartView;
public class HashtagStatusDisplayItem extends StatusDisplayItem{
public final Hashtag tag;
public HashtagStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Hashtag tag){
super(parentID, parentFragment);
this.tag=tag;
}
@Override
public Type getType(){
return Type.HASHTAG;
}
public static class Holder extends StatusDisplayItem.Holder<HashtagStatusDisplayItem>{
private final TextView title, subtitle;
private final HashtagChartView chart;
public Holder(Context context, ViewGroup parent){
super(context, R.layout.item_trending_hashtag, parent);
title=findViewById(R.id.title);
subtitle=findViewById(R.id.subtitle);
chart=findViewById(R.id.chart);
}
@Override
public void onBind(HashtagStatusDisplayItem _item){
Hashtag item=_item.tag;
title.setText('#'+item.name);
int numPeople=item.history.get(0).accounts;
if(item.history.size()>1)
numPeople+=item.history.get(1).accounts;
subtitle.setText(_item.parentFragment.getResources().getQuantityString(R.plurals.x_people_talking, numPeople, numPeople));
chart.setData(item.history);
}
}
}

View File

@@ -30,6 +30,7 @@ public abstract class StatusDisplayItem{
public final String parentID;
public final BaseStatusListFragment parentFragment;
public boolean inset;
public int index;
public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
this.parentID=parentID;
@@ -60,6 +61,8 @@ public abstract class StatusDisplayItem{
case CARD -> new LinkCardStatusDisplayItem.Holder(activity, parent);
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
case ACCOUNT_CARD -> new AccountCardStatusDisplayItem.Holder(activity, parent);
case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent);
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
};
}
@@ -110,8 +113,11 @@ public abstract class StatusDisplayItem{
if(addFooter){
items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID));
}
for(StatusDisplayItem item:items)
int i=1;
for(StatusDisplayItem item:items){
item.inset=inset;
item.index=i++;
}
return items;
}
@@ -135,6 +141,8 @@ public abstract class StatusDisplayItem{
CARD,
FOOTER,
ACCOUNT_CARD,
ACCOUNT,
HASHTAG
}
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.Clickable{

View File

@@ -34,7 +34,7 @@ public class CustomEmojiHelper{
}
public ImageLoaderRequest getImageRequest(int image){
return requests.get(image);
return image<requests.size() ? requests.get(image) : null; // TODO fix this in the image loader
}
public void setImageDrawable(int image, Drawable drawable){

View File

@@ -0,0 +1,32 @@
package org.joinmastodon.android.ui.utils;
import android.view.View;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
public class HideableSingleViewRecyclerAdapter extends SingleViewRecyclerAdapter{
private boolean visible=true;
public HideableSingleViewRecyclerAdapter(View view){
super(view);
}
@Override
public int getItemCount(){
return visible ? 1 : 0;
}
public void setVisible(boolean visible){
if(visible==this.visible)
return;
this.visible=visible;
if(visible)
notifyItemInserted(0);
else
notifyItemRemoved(0);
}
public boolean isVisible(){
return visible;
}
}

View File

@@ -39,12 +39,15 @@ import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import androidx.annotation.AttrRes;
import androidx.annotation.StringRes;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
@@ -311,4 +314,39 @@ public class UiUtils{
.exec(accountID);
}
}
public static <T> void updateList(List<T> oldList, List<T> newList, RecyclerView list, RecyclerView.Adapter<?> adapter, BiPredicate<T, T> areItemsSame){
// Save topmost item position and offset because for some reason RecyclerView would scroll the list to weird places when you insert items at the top
int topItem, topItemOffset;
if(list.getChildCount()==0){
topItem=topItemOffset=0;
}else{
View child=list.getChildAt(0);
topItem=list.getChildAdapterPosition(child);
topItemOffset=child.getTop();
}
DiffUtil.calculateDiff(new DiffUtil.Callback(){
@Override
public int getOldListSize(){
return oldList.size();
}
@Override
public int getNewListSize(){
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition){
return areItemsSame.test(oldList.get(oldItemPosition), newList.get(newItemPosition));
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition){
return true;
}
}).dispatchUpdatesTo(adapter);
list.scrollToPosition(topItem);
list.scrollBy(0, topItemOffset);
}
}

View File

@@ -38,7 +38,7 @@ public class HashtagChartView extends View{
}
public void setData(List<History> data){
int max=0;
int max=1; // avoid dividing by zero
for(History h:data){
max=Math.max(h.accounts, max);
}