Add favorites/reblogs lists and extended footer for ThreadFragment
closes #41, closes #64
This commit is contained in:
parent
e512a7ef90
commit
23ec3e64cf
|
@ -9,8 +9,8 @@ android {
|
||||||
applicationId "org.joinmastodon.android"
|
applicationId "org.joinmastodon.android"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 31
|
targetSdk 31
|
||||||
versionCode 35
|
versionCode 36
|
||||||
versionName "1.0.4"
|
versionName "1.1.0"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ public class StatusInteractionController{
|
||||||
status.favouritesCount++;
|
status.favouritesCount++;
|
||||||
else
|
else
|
||||||
status.favouritesCount--;
|
status.favouritesCount--;
|
||||||
|
E.post(new StatusCountersUpdatedEvent(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReblogged(Status status, boolean reblogged){
|
public void setReblogged(Status status, boolean reblogged){
|
||||||
|
@ -95,5 +96,6 @@ public class StatusInteractionController{
|
||||||
status.reblogsCount++;
|
status.reblogsCount++;
|
||||||
else
|
else
|
||||||
status.reblogsCount--;
|
status.reblogsCount--;
|
||||||
|
E.post(new StatusCountersUpdatedEvent(status));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class GetStatusFavorites extends HeaderPaginationRequest<Account>{
|
||||||
|
public GetStatusFavorites(String id, String maxID, int limit){
|
||||||
|
super(HttpMethod.GET, "/statuses/"+id+"/favourited_by", new TypeToken<>(){});
|
||||||
|
if(maxID!=null)
|
||||||
|
addQueryParameter("max_id", maxID);
|
||||||
|
if(limit>0)
|
||||||
|
addQueryParameter("limit", limit+"");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class GetStatusReblogs extends HeaderPaginationRequest<Account>{
|
||||||
|
public GetStatusReblogs(String id, String maxID, int limit){
|
||||||
|
super(HttpMethod.GET, "/statuses/"+id+"/reblogged_by", new TypeToken<>(){});
|
||||||
|
if(maxID!=null)
|
||||||
|
addQueryParameter("max_id", maxID);
|
||||||
|
if(limit>0)
|
||||||
|
addQueryParameter("limit", limit+"");
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
import org.joinmastodon.android.ui.TileGridLayoutManager;
|
import org.joinmastodon.android.ui.TileGridLayoutManager;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
|
||||||
|
@ -662,7 +663,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||||
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(child);
|
||||||
RecyclerView.ViewHolder siblingHolder=parent.getChildViewHolder(bottomSibling);
|
RecyclerView.ViewHolder siblingHolder=parent.getChildViewHolder(bottomSibling);
|
||||||
if(holder instanceof StatusDisplayItem.Holder<?> ih && siblingHolder instanceof StatusDisplayItem.Holder<?> sh
|
if(holder instanceof StatusDisplayItem.Holder<?> ih && siblingHolder instanceof StatusDisplayItem.Holder<?> sh
|
||||||
&& !ih.getItemID().equals(sh.getItemID()) && ih.getItem().getType()!=StatusDisplayItem.Type.GAP){
|
&& (!ih.getItemID().equals(sh.getItemID()) || sh instanceof ExtendedFooterStatusDisplayItem.Holder) && ih.getItem().getType()!=StatusDisplayItem.Type.GAP){
|
||||||
drawDivider(child, bottomSibling, holder, siblingHolder, parent, c, dividerPaint);
|
drawDivider(child, bottomSibling, holder, siblingHolder, parent, c, dividerPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
package org.joinmastodon.android.fragments;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountFollowing;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
|
||||||
import org.parceler.Parcels;
|
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
|
||||||
|
|
||||||
public class FollowingListFragment extends BaseAccountListFragment{
|
|
||||||
private Account account;
|
|
||||||
private String nextMaxID;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState){
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
account=Parcels.unwrap(getArguments().getParcelable("targetAccount"));
|
|
||||||
setTitle("@"+account.acct);
|
|
||||||
setSubtitle(getResources().getQuantityString(R.plurals.x_following, account.followingCount, account.followingCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume(){
|
|
||||||
super.onResume();
|
|
||||||
if(!loaded && !dataLoading)
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doLoadData(int offset, int count){
|
|
||||||
currentRequest=new GetAccountFollowing(account.id, offset==0 ? null : nextMaxID, count)
|
|
||||||
.setCallback(new SimpleCallback<>(this){
|
|
||||||
@Override
|
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
|
||||||
if(result.nextPageUri!=null)
|
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
|
||||||
else
|
|
||||||
nextMaxID=null;
|
|
||||||
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(accountID);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -45,6 +45,8 @@ import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.account_list.FollowingListFragment;
|
||||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusCreatedEvent;
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusDeletedEvent;
|
import org.joinmastodon.android.events.StatusDeletedEvent;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
@ -90,16 +91,15 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>{
|
||||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
if(holder instanceof FooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){
|
if(holder instanceof FooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){
|
||||||
footer.rebind();
|
footer.rebind();
|
||||||
return;
|
}else if(holder instanceof ExtendedFooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){
|
||||||
|
footer.rebind();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(Status s:preloadedData){
|
for(Status s:preloadedData){
|
||||||
if(s.id.equals(ev.id)){
|
if(s.id.equals(ev.id)){
|
||||||
s.update(ev);
|
s.update(ev);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusContext;
|
import org.joinmastodon.android.model.StatusContext;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
@ -45,7 +47,10 @@ public class ThreadFragment extends StatusListFragment{
|
||||||
for(StatusDisplayItem item:items){
|
for(StatusDisplayItem item:items){
|
||||||
if(item instanceof TextStatusDisplayItem text)
|
if(item instanceof TextStatusDisplayItem text)
|
||||||
text.textSelectable=true;
|
text.textSelectable=true;
|
||||||
|
else if(item instanceof FooterStatusDisplayItem footer)
|
||||||
|
footer.hideCounts=true;
|
||||||
}
|
}
|
||||||
|
items.add(new ExtendedFooterStatusDisplayItem(s.id, this, s.getContentStatus()));
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
|
public abstract class AccountRelatedAccountListFragment extends PaginatedAccountListFragment{
|
||||||
|
protected Account account;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
account=Parcels.unwrap(getArguments().getParcelable("targetAccount"));
|
||||||
|
setTitle("@"+account.acct);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
@ -7,7 +7,6 @@ import android.graphics.drawable.Animatable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.ContextMenu;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -23,6 +22,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
@ -141,14 +141,20 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
||||||
Toolbar toolbar=getToolbar();
|
Toolbar toolbar=getToolbar();
|
||||||
if(toolbar!=null && toolbar.getNavigationIcon()!=null){
|
if(toolbar!=null && toolbar.getNavigationIcon()!=null){
|
||||||
toolbar.setNavigationContentDescription(R.string.back);
|
toolbar.setNavigationContentDescription(R.string.back);
|
||||||
toolbar.setTitleTextAppearance(getActivity(), R.style.m3_title_medium);
|
if(hasSubtitle()){
|
||||||
toolbar.setSubtitleTextAppearance(getActivity(), R.style.m3_body_medium);
|
toolbar.setTitleTextAppearance(getActivity(), R.style.m3_title_medium);
|
||||||
int color=UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary);
|
toolbar.setSubtitleTextAppearance(getActivity(), R.style.m3_body_medium);
|
||||||
toolbar.setTitleTextColor(color);
|
int color=UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary);
|
||||||
toolbar.setSubtitleTextColor(color);
|
toolbar.setTitleTextColor(color);
|
||||||
|
toolbar.setSubtitleTextColor(color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean hasSubtitle(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplyWindowInsets(WindowInsets insets){
|
public void onApplyWindowInsets(WindowInsets insets){
|
||||||
if(Build.VERSION.SDK_INT>=29 && insets.getTappableElementInsets().bottom==0){
|
if(Build.VERSION.SDK_INT>=29 && insets.getTappableElementInsets().bottom==0){
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.GetAccountFollowers;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class FollowerListFragment extends AccountRelatedAccountListFragment{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setSubtitle(getResources().getQuantityString(R.plurals.x_followers, account.followersCount, account.followersCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
|
return new GetAccountFollowers(account.id, maxID, count);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.GetAccountFollowing;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class FollowingListFragment extends AccountRelatedAccountListFragment{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setSubtitle(getResources().getQuantityString(R.plurals.x_following, account.followingCount, account.followingCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
|
return new GetAccountFollowing(account.id, maxID, count);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,39 +1,21 @@
|
||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountFollowers;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.parceler.Parcels;
|
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
public class FollowerListFragment extends BaseAccountListFragment{
|
public abstract class PaginatedAccountListFragment extends BaseAccountListFragment{
|
||||||
private Account account;
|
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
|
|
||||||
@Override
|
public abstract HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count);
|
||||||
public void onCreate(Bundle savedInstanceState){
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
account=Parcels.unwrap(getArguments().getParcelable("targetAccount"));
|
|
||||||
setTitle("@"+account.acct);
|
|
||||||
setSubtitle(getResources().getQuantityString(R.plurals.x_followers, account.followersCount, account.followersCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume(){
|
|
||||||
super.onResume();
|
|
||||||
if(!loaded && !dataLoading)
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetAccountFollowers(account.id, offset==0 ? null : nextMaxID, count)
|
currentRequest=onCreateRequest(offset==0 ? null : nextMaxID, count)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
public void onSuccess(HeaderPaginationList<Account> result){
|
||||||
|
@ -46,4 +28,11 @@ public class FollowerListFragment extends BaseAccountListFragment{
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume(){
|
||||||
|
super.onResume();
|
||||||
|
if(!loaded && !dataLoading)
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.GetStatusFavorites;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class StatusFavoritesListFragment extends StatusRelatedAccountListFragment{
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setTitle(getResources().getQuantityString(R.plurals.x_favorites, status.favouritesCount, status.favouritesCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
|
return new GetStatusFavorites(status.id, maxID, count);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.GetStatusReblogs;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
|
||||||
|
public class StatusReblogsListFragment extends StatusRelatedAccountListFragment{
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setTitle(getResources().getQuantityString(R.plurals.x_reblogs, status.reblogsCount, status.reblogsCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
||||||
|
return new GetStatusReblogs(status.id, maxID, count);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
|
public abstract class StatusRelatedAccountListFragment extends PaginatedAccountListFragment{
|
||||||
|
protected Status status;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
status=Parcels.unwrap(getArguments().getParcelable("status"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean hasSubtitle(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ForegroundColorSpan;
|
||||||
|
import android.text.style.TypefaceSpan;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.account_list.StatusFavoritesListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.account_list.StatusReblogsListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.account_list.StatusRelatedAccountListFragment;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.FormatStyle;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import androidx.annotation.PluralsRes;
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
|
|
||||||
|
public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
|
public final Status status;
|
||||||
|
|
||||||
|
private static final DateTimeFormatter TIME_FORMATTER=DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT);
|
||||||
|
|
||||||
|
public ExtendedFooterStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status){
|
||||||
|
super(parentID, parentFragment);
|
||||||
|
this.status=status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType(){
|
||||||
|
return Type.EXTENDED_FOOTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Holder extends StatusDisplayItem.Holder<ExtendedFooterStatusDisplayItem>{
|
||||||
|
private final TextView reblogs, favorites, time;
|
||||||
|
private final View buttonsView;
|
||||||
|
|
||||||
|
public Holder(Context context, ViewGroup parent){
|
||||||
|
super(context, R.layout.display_item_extended_footer, parent);
|
||||||
|
reblogs=findViewById(R.id.reblogs);
|
||||||
|
favorites=findViewById(R.id.favorites);
|
||||||
|
time=findViewById(R.id.timestamp);
|
||||||
|
buttonsView=findViewById(R.id.button_bar);
|
||||||
|
|
||||||
|
reblogs.setOnClickListener(v->startAccountListFragment(StatusReblogsListFragment.class));
|
||||||
|
favorites.setOnClickListener(v->startAccountListFragment(StatusFavoritesListFragment.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(ExtendedFooterStatusDisplayItem item){
|
||||||
|
Status s=item.status;
|
||||||
|
if(s.favouritesCount>0){
|
||||||
|
favorites.setVisibility(View.VISIBLE);
|
||||||
|
favorites.setText(getFormattedPlural(R.plurals.x_favorites, s.favouritesCount));
|
||||||
|
}else{
|
||||||
|
favorites.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if(s.reblogsCount>0){
|
||||||
|
reblogs.setVisibility(View.VISIBLE);
|
||||||
|
reblogs.setText(getFormattedPlural(R.plurals.x_reblogs, s.reblogsCount));
|
||||||
|
}else{
|
||||||
|
reblogs.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
if(s.favouritesCount==0 && s.reblogsCount==0){
|
||||||
|
buttonsView.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
buttonsView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
String timeStr=TIME_FORMATTER.format(item.status.createdAt.atZone(ZoneId.systemDefault()));
|
||||||
|
if(item.status.application!=null && !TextUtils.isEmpty(item.status.application.name)){
|
||||||
|
time.setText(item.parentFragment.getString(R.string.timestamp_via_app, timeStr, item.status.application.name));
|
||||||
|
}else{
|
||||||
|
time.setText(timeStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SpannableStringBuilder getFormattedPlural(@PluralsRes int res, int quantity){
|
||||||
|
String str=item.parentFragment.getResources().getQuantityString(res, quantity, quantity);
|
||||||
|
String formattedNumber=String.format(Locale.getDefault(), "%,d", quantity);
|
||||||
|
int index=str.indexOf(formattedNumber);
|
||||||
|
SpannableStringBuilder ssb=new SpannableStringBuilder(str);
|
||||||
|
if(index>=0){
|
||||||
|
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), index, index+formattedNumber.length(), 0);
|
||||||
|
ssb.setSpan(new ForegroundColorSpan(UiUtils.getThemeColor(item.parentFragment.getActivity(), android.R.attr.textColorPrimary)), index, index+formattedNumber.length(), 0);
|
||||||
|
}
|
||||||
|
return ssb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startAccountListFragment(Class<? extends StatusRelatedAccountListFragment> cls){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", item.parentFragment.getAccountID());
|
||||||
|
args.putParcelable("status", Parcels.wrap(item.status));
|
||||||
|
Nav.go(item.parentFragment.getActivity(), cls, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import me.grishka.appkit.utils.V;
|
||||||
public class FooterStatusDisplayItem extends StatusDisplayItem{
|
public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
public final Status status;
|
public final Status status;
|
||||||
private final String accountID;
|
private final String accountID;
|
||||||
|
public boolean hideCounts;
|
||||||
|
|
||||||
public FooterStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status, String accountID){
|
public FooterStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status, String accountID){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
|
@ -91,7 +92,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindButton(TextView btn, int count){
|
private void bindButton(TextView btn, int count){
|
||||||
if(count>0){
|
if(count>0 && !item.hideCounts){
|
||||||
btn.setText(DecimalFormat.getIntegerInstance().format(count));
|
btn.setText(DecimalFormat.getIntegerInstance().format(count));
|
||||||
btn.setCompoundDrawablePadding(V.dp(8));
|
btn.setCompoundDrawablePadding(V.dp(8));
|
||||||
}else{
|
}else{
|
||||||
|
|
|
@ -64,6 +64,7 @@ public abstract class StatusDisplayItem{
|
||||||
case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent);
|
case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent);
|
||||||
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
|
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
|
||||||
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
|
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
|
||||||
|
case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +147,8 @@ public abstract class StatusDisplayItem{
|
||||||
ACCOUNT_CARD,
|
ACCOUNT_CARD,
|
||||||
ACCOUNT,
|
ACCOUNT,
|
||||||
HASHTAG,
|
HASHTAG,
|
||||||
GAP
|
GAP,
|
||||||
|
EXTENDED_FOOTER
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
|
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
|
||||||
|
|
|
@ -10,6 +10,7 @@ import android.content.res.TypedArray;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Typeface;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.InsetDrawable;
|
import android.graphics.drawable.InsetDrawable;
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:attr/colorControlHighlight">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
<corners android:radius="4dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?colorBackgroundLightest">
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||||
|
android:id="@+id/button_bar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/reblogs"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:minHeight="36dp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:background="@drawable/bg_text_button"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
tools:text="4 reblogs"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/favorites"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:minHeight="36dp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:background="@drawable/bg_text_button"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
tools:text="12 favorites"/>
|
||||||
|
|
||||||
|
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/timestamp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:minHeight="20dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
tools:text="Dec 12, 2021, 12:42 PM via Mastodon for Android"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -338,4 +338,5 @@
|
||||||
<item quantity="one">%,d reblog</item>
|
<item quantity="one">%,d reblog</item>
|
||||||
<item quantity="other">%,d reblogs</item>
|
<item quantity="other">%,d reblogs</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="timestamp_via_app">%1$s via %2$s</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue