Notifications

This commit is contained in:
Grishka 2022-01-24 02:56:57 +03:00
parent c3b7fb7002
commit a4a514d37a
17 changed files with 448 additions and 271 deletions

View File

@ -0,0 +1,18 @@
package org.joinmastodon.android.api.requests.notifications;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Notification;
import java.util.List;
public class GetNotifications extends MastodonAPIRequest<List<Notification>>{
public GetNotifications(String maxID, int limit){
super(HttpMethod.GET, "/notifications", new TypeToken<>(){});
if(maxID!=null)
addQueryParameter("max_id", maxID);
if(limit>0)
addQueryParameter("limit", ""+limit);
}
}

View File

@ -0,0 +1,251 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import org.joinmastodon.android.model.DisplayItemsParent;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.PhotoStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;
public abstract class BaseStatusListFragment<T extends DisplayItemsParent> extends BaseRecyclerFragment<T> implements PhotoViewerHost{
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
protected DisplayItemsAdapter adapter;
protected String accountID;
protected PhotoViewer currentPhotoViewer;
public BaseStatusListFragment(){
super(20);
}
@Override
protected RecyclerView.Adapter getAdapter(){
return adapter=new DisplayItemsAdapter();
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
accountID=getArguments().getString("account");
}
@Override
public void onAppendItems(List<T> items){
super.onAppendItems(items);
for(T s:items){
displayItems.addAll(buildDisplayItems(s));
}
}
@Override
public void onClearItems(){
super.onClearItems();
displayItems.clear();
}
protected void prependItems(List<T> items){
data.addAll(0, items);
int offset=0;
for(T s:items){
List<StatusDisplayItem> toAdd=buildDisplayItems(s);
displayItems.addAll(offset, toAdd);
offset+=toAdd.size();
}
adapter.notifyItemRangeInserted(0, offset);
}
protected String getMaxID(){
if(!preloadedData.isEmpty())
return preloadedData.get(preloadedData.size()-1).getID();
else if(!data.isEmpty())
return data.get(data.size()-1).getID();
else
return null;
}
protected abstract List<StatusDisplayItem> buildDisplayItems(T s);
@Override
protected void onHidden(){
super.onHidden();
// Clear any loaded images from the list to make it possible for the GC to deallocate them.
// The delay avoids blank image views showing up in the app switcher.
content.postDelayed(()->{
if(!isHidden())
return;
imgLoader.deactivate();
UsableRecyclerView list=(UsableRecyclerView) this.list;
for(int i=0; i<list.getChildCount(); i++){
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
if(holder instanceof ImageLoaderViewHolder){
for(int j=0; j<list.getImageCountForItem(holder.getAbsoluteAdapterPosition()); j++){
((ImageLoaderViewHolder) holder).clearImage(j);
}
}
}
}, 100);
}
@Override
protected void onShown(){
super.onShown();
imgLoader.activate();
}
@Override
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex){
final Status status=_status.reblog!=null ? _status.reblog : _status;
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
private PhotoStatusDisplayItem.Holder transitioningHolder;
@Override
public void setPhotoViewVisibility(int index, boolean visible){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null)
holder.photo.setAlpha(visible ? 1f : 0f);
}
@Override
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null){
transitioningHolder=holder;
View view=transitioningHolder.photo;
int[] pos={0, 0};
view.getLocationOnScreen(pos);
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
list.setClipChildren(false);
transitioningHolder.itemView.setElevation(1f);
return true;
}
return false;
}
@Override
public void setTransitioningViewTransform(float translateX, float translateY, float scale){
View view=transitioningHolder.photo;
view.setTranslationX(translateX);
view.setTranslationY(translateY);
view.setScaleX(scale);
view.setScaleY(scale);
}
@Override
public void endPhotoViewTransition(){
View view=transitioningHolder.photo;
view.setTranslationX(0f);
view.setTranslationY(0f);
view.setScaleX(1f);
view.setScaleY(1f);
transitioningHolder.itemView.setElevation(0f);
list.setClipChildren(true);
transitioningHolder=null;
}
@Override
public Drawable getPhotoViewCurrentDrawable(int index){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null)
return holder.photo.getDrawable();
return null;
}
@Override
public void photoViewerDismissed(){
currentPhotoViewer=null;
}
private PhotoStatusDisplayItem.Holder findPhotoViewHolder(int index){
int offset=0;
for(StatusDisplayItem item:displayItems){
if(item.parentID.equals(parentID)){
if(item instanceof PhotoStatusDisplayItem){
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(getMainAdapterOffset()+offset+index);
if(holder instanceof PhotoStatusDisplayItem.Holder){
return (PhotoStatusDisplayItem.Holder) holder;
}
return null;
}
}
offset++;
}
return null;
}
});
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
if(currentPhotoViewer!=null)
currentPhotoViewer.offsetView(-dx, -dy);
}
});
}
protected int getMainAdapterOffset(){
return 0;
}
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
public DisplayItemsAdapter(){
super(imgLoader);
}
@NonNull
@Override
public BindableViewHolder<StatusDisplayItem> onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return (BindableViewHolder<StatusDisplayItem>) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType], getActivity(), parent);
}
@Override
public void onBindViewHolder(BindableViewHolder<StatusDisplayItem> holder, int position){
holder.bind(displayItems.get(position));
super.onBindViewHolder(holder, position);
}
@Override
public int getItemCount(){
return displayItems.size();
}
@Override
public int getItemViewType(int position){
return displayItems.get(position).getType().ordinal();
}
@Override
public int getImageCountForItem(int position){
return displayItems.get(position).getImageCount();
}
@Override
public ImageLoaderRequest getImageRequest(int position, int image){
return displayItems.get(position).getImageRequest(image);
}
}
}

View File

@ -11,8 +11,10 @@ import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.model.Status;
import org.parceler.Parcels;
import java.util.Collections;
import java.util.List;
@ -32,14 +34,7 @@ public class HomeTimelineFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
String maxID;
if(offset>0 && !preloadedData.isEmpty())
maxID=preloadedData.get(preloadedData.size()-1).id;
else if(offset>0 && !data.isEmpty())
maxID=data.get(data.size()-1).id;
else
maxID=null;
new GetHomeTimeline(maxID, null, count)
new GetHomeTimeline(offset>0 ? getMaxID() : null, null, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
@ -51,14 +46,22 @@ public class HomeTimelineFragment extends StatusListFragment{
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
menu.add("New toot");
inflater.inflate(R.menu.home, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), CreateTootFragment.class, args);
int id=item.getItemId();
if(id==R.id.new_toot){
Nav.go(getActivity(), CreateTootFragment.class, args);
}else if(id==R.id.notifications){
Nav.go(getActivity(), NotificationsFragment.class, args);
}else if(id==R.id.my_profile){
args.putParcelable("profileAccount", Parcels.wrap(AccountSessionManager.getInstance().getAccount(accountID).self));
Nav.go(getActivity(), ProfileFragment.class, args);
}
return true;
}

View File

@ -0,0 +1,56 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import me.grishka.appkit.api.SimpleCallback;
public class NotificationsFragment extends BaseStatusListFragment<Notification>{
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
setTitle(R.string.notifications);
loadData();
}
@Override
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
ReblogOrReplyLineStatusDisplayItem titleItem=new ReblogOrReplyLineStatusDisplayItem(n.id, switch(n.type){
case FOLLOW -> getString(R.string.user_followed_you, n.account.displayName);
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request, n.account.displayName);
case MENTION -> getString(R.string.user_mentioned_you, n.account.displayName);
case REBLOG -> getString(R.string.user_boosted, n.account.displayName);
case FAVORITE -> getString(R.string.user_favorited, n.account.displayName);
case POLL -> getString(R.string.poll_ended);
case STATUS -> getString(R.string.user_posted, n.account.displayName);
});
if(n.status!=null){
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n);
items.add(0, titleItem);
return items;
}else{
return Collections.singletonList(titleItem);
}
}
@Override
protected void doLoadData(int offset, int count){
new GetNotifications(offset>0 ? getMaxID() : null, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Notification> result){
onDataLoaded(result, !result.isEmpty());
}
})
.exec(accountID);
}
}

View File

@ -1,7 +1,6 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.os.Bundle;
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
import org.joinmastodon.android.model.Account;
@ -25,14 +24,7 @@ public class ProfileFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
String maxID;
if(offset>0 && !preloadedData.isEmpty())
maxID=preloadedData.get(preloadedData.size()-1).id;
else if(offset>0 && !data.isEmpty())
maxID=data.get(data.size()-1).id;
else
maxID=null;
currentRequest=new GetAccountStatuses(user.id, maxID, null, count)
currentRequest=new GetAccountStatuses(user.id, offset>0 ? getMaxID() : null, null, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){

View File

@ -1,233 +1,12 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.PhotoStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;
public abstract class StatusListFragment extends BaseRecyclerFragment<Status> implements PhotoViewerHost{
protected ArrayList<StatusDisplayItem> displayItems=new ArrayList<>();
protected DisplayItemsAdapter adapter;
protected String accountID;
protected PhotoViewer currentPhotoViewer;
public StatusListFragment(){
super(20);
}
@Override
protected RecyclerView.Adapter getAdapter(){
return adapter=new DisplayItemsAdapter();
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
accountID=getArguments().getString("account");
}
@Override
public void onAppendItems(List<Status> items){
super.onAppendItems(items);
for(Status s:items){
displayItems.addAll(StatusDisplayItem.buildItems(this, s, accountID));
}
}
@Override
public void onClearItems(){
super.onClearItems();
displayItems.clear();
}
protected void prependItems(List<Status> items){
data.addAll(0, items);
int offset=0;
for(Status s:items){
List<StatusDisplayItem> toAdd=StatusDisplayItem.buildItems(this, s, accountID);
displayItems.addAll(offset, toAdd);
offset+=toAdd.size();
}
adapter.notifyItemRangeInserted(0, offset);
}
@Override
protected void onHidden(){
super.onHidden();
imgLoader.deactivate();
UsableRecyclerView list=(UsableRecyclerView) this.list;
for(int i=0;i<list.getChildCount();i++){
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
if(holder instanceof ImageLoaderViewHolder){
for(int j=0;j<list.getImageCountForItem(holder.getAbsoluteAdapterPosition());j++){
((ImageLoaderViewHolder) holder).clearImage(j);
}
}
}
}
@Override
protected void onShown(){
super.onShown();
imgLoader.activate();
}
@Override
public void openPhotoViewer(Status _status, int attachmentIndex){
final Status status=_status.reblog!=null ? _status.reblog : _status;
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
private PhotoStatusDisplayItem.Holder transitioningHolder;
@Override
public void setPhotoViewVisibility(int index, boolean visible){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null)
holder.photo.setAlpha(visible ? 1f : 0f);
}
@Override
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null){
transitioningHolder=holder;
View view=transitioningHolder.photo;
int[] pos={0, 0};
view.getLocationOnScreen(pos);
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
list.setClipChildren(false);
transitioningHolder.itemView.setElevation(1f);
return true;
}
return false;
}
@Override
public void setTransitioningViewTransform(float translateX, float translateY, float scale){
View view=transitioningHolder.photo;
view.setTranslationX(translateX);
view.setTranslationY(translateY);
view.setScaleX(scale);
view.setScaleY(scale);
}
@Override
public void endPhotoViewTransition(){
View view=transitioningHolder.photo;
view.setTranslationX(0f);
view.setTranslationY(0f);
view.setScaleX(1f);
view.setScaleY(1f);
transitioningHolder.itemView.setElevation(0f);
list.setClipChildren(true);
transitioningHolder=null;
}
@Override
public Drawable getPhotoViewCurrentDrawable(int index){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null)
return holder.photo.getDrawable();
return null;
}
@Override
public void photoViewerDismissed(){
currentPhotoViewer=null;
}
private PhotoStatusDisplayItem.Holder findPhotoViewHolder(int index){
int offset=0;
for(StatusDisplayItem item:displayItems){
if(item.status==_status){
if(item instanceof PhotoStatusDisplayItem){
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(getMainAdapterOffset()+offset+index);
if(holder instanceof PhotoStatusDisplayItem.Holder){
return (PhotoStatusDisplayItem.Holder) holder;
}
return null;
}
}
offset++;
}
return null;
}
});
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
if(currentPhotoViewer!=null)
currentPhotoViewer.offsetView(-dx, -dy);
}
});
}
protected int getMainAdapterOffset(){
return 0;
}
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
public DisplayItemsAdapter(){
super(imgLoader);
}
@NonNull
@Override
public BindableViewHolder<StatusDisplayItem> onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return (BindableViewHolder<StatusDisplayItem>) StatusDisplayItem.createViewHolder(StatusDisplayItem.Type.values()[viewType], getActivity(), parent);
}
@Override
public void onBindViewHolder(BindableViewHolder<StatusDisplayItem> holder, int position){
holder.bind(displayItems.get(position));
super.onBindViewHolder(holder, position);
}
@Override
public int getItemCount(){
return displayItems.size();
}
@Override
public int getItemViewType(int position){
return displayItems.get(position).getType().ordinal();
}
@Override
public int getImageCountForItem(int position){
return displayItems.get(position).getImageCount();
}
@Override
public ImageLoaderRequest getImageRequest(int position, int image){
return displayItems.get(position).getImageRequest(image);
}
public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
protected List<StatusDisplayItem> buildDisplayItems(Status s){
return StatusDisplayItem.buildItems(this, s, accountID, s);
}
}

View File

@ -0,0 +1,8 @@
package org.joinmastodon.android.model;
/**
* A model object from which {@link org.joinmastodon.android.ui.displayitems.StatusDisplayItem}s can be generated.
*/
public interface DisplayItemsParent{
String getID();
}

View File

@ -0,0 +1,51 @@
package org.joinmastodon.android.model;
import com.google.gson.annotations.SerializedName;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import java.time.Instant;
public class Notification extends BaseModel implements DisplayItemsParent{
@RequiredField
public String id;
@RequiredField
public Type type;
@RequiredField
public Instant createdAt;
@RequiredField
public Account account;
public Status status;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
account.postprocess();
if(status!=null)
status.postprocess();
}
@Override
public String getID(){
return id;
}
public enum Type{
@SerializedName("follow")
FOLLOW,
@SerializedName("follow_request")
FOLLOW_REQUEST,
@SerializedName("mention")
MENTION,
@SerializedName("reblog")
REBLOG,
@SerializedName("favourite")
FAVORITE,
@SerializedName("poll")
POLL,
@SerializedName("status")
STATUS
}
}

View File

@ -1,17 +1,12 @@
package org.joinmastodon.android.model;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.joinmastodon.android.ui.text.HtmlParser;
import java.time.Instant;
import java.util.List;
public class Status extends BaseModel{
public class Status extends BaseModel implements DisplayItemsParent{
@RequiredField
public String id;
@RequiredField
@ -111,4 +106,9 @@ public class Status extends BaseModel{
", pinned="+pinned+
'}';
}
@Override
public String getID(){
return id;
}
}

View File

@ -31,8 +31,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
private Fragment parentFragment;
private String accountID;
public HeaderStatusDisplayItem(Status status, Account user, Instant createdAt, Fragment parentFragment, String accountID){
super(status);
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, Fragment parentFragment, String accountID){
super(parentID);
this.user=user;
this.createdAt=createdAt;
avaRequest=new UrlImageLoaderRequest(user.avatar);

View File

@ -21,8 +21,10 @@ public class PhotoStatusDisplayItem extends StatusDisplayItem{
private Attachment attachment;
private ImageLoaderRequest request;
private Fragment parentFragment;
public PhotoStatusDisplayItem(Status status, Attachment photo, Fragment parentFragment){
super(status);
private Status status;
public PhotoStatusDisplayItem(String parentID, Status status, Attachment photo, Fragment parentFragment){
super(parentID);
this.status=status;
this.attachment=photo;
request=new UrlImageLoaderRequest(photo.url, 1000, 1000);
this.parentFragment=parentFragment;
@ -69,7 +71,7 @@ public class PhotoStatusDisplayItem extends StatusDisplayItem{
private void onViewClick(View v){
if(item.parentFragment instanceof PhotoViewerHost){
Status contentStatus=item.status.reblog!=null ? item.status.reblog : item.status;
((PhotoViewerHost) item.parentFragment).openPhotoViewer(item.status, contentStatus.mediaAttachments.indexOf(item.attachment));
((PhotoViewerHost) item.parentFragment).openPhotoViewer(item.parentID, item.status, contentStatus.mediaAttachments.indexOf(item.attachment));
}
}
}

View File

@ -10,8 +10,11 @@ import org.joinmastodon.android.model.Status;
import me.grishka.appkit.utils.BindableViewHolder;
public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
public ReblogOrReplyLineStatusDisplayItem(Status status){
super(status);
private CharSequence text;
public ReblogOrReplyLineStatusDisplayItem(String parentID, CharSequence text){
super(parentID);
this.text=text;
}
@Override
@ -28,9 +31,7 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(ReblogOrReplyLineStatusDisplayItem item){
if(item.status.reblog!=null){
text.setText(itemView.getContext().getString(R.string.user_boosted, item.status.account.displayName));
}
text.setText(item.text);
}
}
}

View File

@ -5,21 +5,22 @@ import android.app.Fragment;
import android.text.TextUtils;
import android.view.ViewGroup;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.DisplayItemsParent;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.text.HtmlParser;
import java.util.ArrayList;
import java.util.List;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
public abstract class StatusDisplayItem{
public final Status status;
public final String parentID;
public StatusDisplayItem(Status status){
this.status=status;
public StatusDisplayItem(String parentID){
this.parentID=parentID;
}
public abstract Type getType();
@ -42,18 +43,19 @@ public abstract class StatusDisplayItem{
};
}
public static List<StatusDisplayItem> buildItems(Fragment fragment, Status status, String accountID){
public static ArrayList<StatusDisplayItem> buildItems(Fragment fragment, Status status, String accountID, DisplayItemsParent parentObject){
String parentID=parentObject.getID();
ArrayList<StatusDisplayItem> items=new ArrayList<>();
Status statusForContent=status.reblog==null ? status : status.reblog;
if(status.reblog!=null){
items.add(new ReblogOrReplyLineStatusDisplayItem(status));
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment.getString(R.string.user_boosted, status.account.displayName)));
}
items.add(new HeaderStatusDisplayItem(status, statusForContent.account, statusForContent.createdAt, fragment, accountID));
items.add(new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID));
if(!TextUtils.isEmpty(statusForContent.content))
items.add(new TextStatusDisplayItem(status, HtmlParser.parse(statusForContent.content, statusForContent.emojis), fragment));
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis), fragment));
for(Attachment attachment:statusForContent.mediaAttachments){
if(attachment.type==Attachment.Type.IMAGE){
items.add(new PhotoStatusDisplayItem(status, attachment, fragment));
items.add(new PhotoStatusDisplayItem(parentID, status, attachment, fragment));
}
}
return items;

View File

@ -26,8 +26,8 @@ public class TextStatusDisplayItem extends StatusDisplayItem implements LinkSpan
private CharSequence text;
private ImageLoaderRequest[] emojiRequests;
private Fragment parentFragment;
public TextStatusDisplayItem(Status status, CharSequence text, Fragment parentFragment){
super(status);
public TextStatusDisplayItem(String parentID, CharSequence text, Fragment parentFragment){
super(parentID);
this.text=text;
this.parentFragment=parentFragment;
if(text instanceof Spanned){

View File

@ -3,5 +3,5 @@ package org.joinmastodon.android.ui.photoviewer;
import org.joinmastodon.android.model.Status;
public interface PhotoViewerHost{
void openPhotoViewer(Status status, int attachmentIndex);
void openPhotoViewer(String parentID, Status status, int attachmentIndex);
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/new_toot" android:title="New toot"/>
<item android:id="@+id/notifications" android:title="@string/notifications"/>
<item android:id="@+id/my_profile" android:title="My profile"/>
</menu>

View File

@ -12,4 +12,12 @@
<string name="finishing_auth">Finishing authentication…</string>
<string name="user_boosted">%s boosted</string>
<string name="replied_to">Replied to %s</string>
<string name="notifications">Notifications</string>
<string name="user_followed_you">%s followed you</string>
<string name="user_sent_follow_request">%s sent you a follow request</string>
<string name="user_mentioned_you">%s mentioned you</string>
<string name="user_favorited">%s favorited your toot</string>
<string name="poll_ended">Poll you voted in has ended</string>
<string name="user_posted">%s posted</string>
</resources>