From 23ec3e64cf3f938258b8f2f0e7d23e6c033e644d Mon Sep 17 00:00:00 2001 From: Grishka Date: Wed, 4 May 2022 01:04:59 +0300 Subject: [PATCH] Add favorites/reblogs lists and extended footer for ThreadFragment closes #41, closes #64 --- mastodon/build.gradle | 4 +- .../api/StatusInteractionController.java | 2 + .../requests/statuses/GetStatusFavorites.java | 16 +++ .../requests/statuses/GetStatusReblogs.java | 16 +++ .../fragments/BaseStatusListFragment.java | 3 +- .../fragments/FollowingListFragment.java | 49 -------- .../android/fragments/ProfileFragment.java | 2 + .../android/fragments/StatusListFragment.java | 6 +- .../android/fragments/ThreadFragment.java | 5 + .../AccountRelatedAccountListFragment.java | 17 +++ .../BaseAccountListFragment.java | 20 ++-- .../account_list/FollowerListFragment.java | 22 ++++ .../account_list/FollowingListFragment.java | 22 ++++ .../PaginatedAccountListFragment.java} | 35 ++---- .../StatusFavoritesListFragment.java | 21 ++++ .../StatusReblogsListFragment.java | 21 ++++ .../StatusRelatedAccountListFragment.java | 21 ++++ .../ExtendedFooterStatusDisplayItem.java | 112 ++++++++++++++++++ .../displayitems/FooterStatusDisplayItem.java | 3 +- .../ui/displayitems/StatusDisplayItem.java | 4 +- .../android/ui/utils/UiUtils.java | 1 + .../src/main/res/drawable/bg_text_button.xml | 9 ++ .../layout/display_item_extended_footer.xml | 54 +++++++++ mastodon/src/main/res/values/strings.xml | 1 + 24 files changed, 379 insertions(+), 87 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusFavorites.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusReblogs.java delete mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/FollowingListFragment.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java rename mastodon/src/main/java/org/joinmastodon/android/fragments/{ => account_list}/BaseAccountListFragment.java (96%) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowerListFragment.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java rename mastodon/src/main/java/org/joinmastodon/android/fragments/{FollowerListFragment.java => account_list/PaginatedAccountListFragment.java} (52%) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java create mode 100644 mastodon/src/main/res/drawable/bg_text_button.xml create mode 100644 mastodon/src/main/res/layout/display_item_extended_footer.xml diff --git a/mastodon/build.gradle b/mastodon/build.gradle index d0fcf491..d031144b 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -9,8 +9,8 @@ android { applicationId "org.joinmastodon.android" minSdk 23 targetSdk 31 - versionCode 35 - versionName "1.0.4" + versionCode 36 + versionName "1.1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java b/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java index 7836ad95..a2a72e90 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java @@ -58,6 +58,7 @@ public class StatusInteractionController{ status.favouritesCount++; else status.favouritesCount--; + E.post(new StatusCountersUpdatedEvent(status)); } public void setReblogged(Status status, boolean reblogged){ @@ -95,5 +96,6 @@ public class StatusInteractionController{ status.reblogsCount++; else status.reblogsCount--; + E.post(new StatusCountersUpdatedEvent(status)); } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusFavorites.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusFavorites.java new file mode 100644 index 00000000..9ba0be6b --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusFavorites.java @@ -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{ + 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+""); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusReblogs.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusReblogs.java new file mode 100644 index 00000000..41d5d77c --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusReblogs.java @@ -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{ + 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+""); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 693d3483..57e8c3e2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -31,6 +31,7 @@ import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.BetterItemAnimator; import org.joinmastodon.android.ui.PhotoLayoutHelper; 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.HeaderStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem; @@ -662,7 +663,7 @@ public abstract class BaseStatusListFragment exten RecyclerView.ViewHolder holder=parent.getChildViewHolder(child); RecyclerView.ViewHolder siblingHolder=parent.getChildViewHolder(bottomSibling); 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); } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/FollowingListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/FollowingListFragment.java deleted file mode 100644 index d11ca1b6..00000000 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/FollowingListFragment.java +++ /dev/null @@ -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 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); - } -} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index e8a69432..eb602005 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -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.UpdateAccountCredentials; 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.model.Account; import org.joinmastodon.android.model.AccountField; diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java index a1cf45c3..98721088 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java @@ -10,6 +10,7 @@ import org.joinmastodon.android.events.StatusCountersUpdatedEvent; import org.joinmastodon.android.events.StatusCreatedEvent; import org.joinmastodon.android.events.StatusDeletedEvent; 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.HeaderStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.StatusDisplayItem; @@ -90,16 +91,15 @@ public abstract class StatusListFragment extends BaseStatusListFragment{ RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i)); if(holder instanceof FooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){ footer.rebind(); - return; + }else if(holder instanceof ExtendedFooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){ + footer.rebind(); } } - return; } } for(Status s:preloadedData){ if(s.id.equals(ev.id)){ s.update(ev); - return; } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index f2b540ad..ec49a3ab 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -11,6 +11,8 @@ import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Status; 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.TextStatusDisplayItem; import org.joinmastodon.android.ui.text.HtmlParser; @@ -45,7 +47,10 @@ public class ThreadFragment extends StatusListFragment{ for(StatusDisplayItem item:items){ if(item instanceof TextStatusDisplayItem text) text.textSelectable=true; + else if(item instanceof FooterStatusDisplayItem footer) + footer.hideCounts=true; } + items.add(new ExtendedFooterStatusDisplayItem(s.id, this, s.getContentStatus())); } return items; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java new file mode 100644 index 00000000..8277a5b4 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java @@ -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); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java similarity index 96% rename from mastodon/src/main/java/org/joinmastodon/android/fragments/BaseAccountListFragment.java rename to mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java index b9d5e58d..ce898549 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseAccountListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java @@ -1,4 +1,4 @@ -package org.joinmastodon.android.fragments; +package org.joinmastodon.android.fragments.account_list; import android.app.ProgressDialog; import android.content.Intent; @@ -7,7 +7,6 @@ import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; -import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -23,6 +22,7 @@ import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships; 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.model.Account; import org.joinmastodon.android.model.Relationship; @@ -141,14 +141,20 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment=29 && insets.getTappableElementInsets().bottom==0){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowerListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowerListFragment.java new file mode 100644 index 00000000..3642fbac --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowerListFragment.java @@ -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 onCreateRequest(String maxID, int count){ + return new GetAccountFollowers(account.id, maxID, count); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java new file mode 100644 index 00000000..d1daf999 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java @@ -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 onCreateRequest(String maxID, int count){ + return new GetAccountFollowing(account.id, maxID, count); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/FollowerListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java similarity index 52% rename from mastodon/src/main/java/org/joinmastodon/android/fragments/FollowerListFragment.java rename to mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java index 3ba63614..a89a7dcd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/FollowerListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java @@ -1,39 +1,21 @@ -package org.joinmastodon.android.fragments; +package org.joinmastodon.android.fragments.account_list; -import android.os.Bundle; - -import org.joinmastodon.android.R; -import org.joinmastodon.android.api.requests.accounts.GetAccountFollowers; +import org.joinmastodon.android.api.requests.HeaderPaginationRequest; 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 FollowerListFragment extends BaseAccountListFragment{ - private Account account; +public abstract class PaginatedAccountListFragment extends BaseAccountListFragment{ 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_followers, account.followersCount, account.followersCount)); - } - - @Override - public void onResume(){ - super.onResume(); - if(!loaded && !dataLoading) - loadData(); - } + public abstract HeaderPaginationRequest onCreateRequest(String maxID, int count); @Override 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){ @Override public void onSuccess(HeaderPaginationList result){ @@ -46,4 +28,11 @@ public class FollowerListFragment extends BaseAccountListFragment{ }) .exec(accountID); } + + @Override + public void onResume(){ + super.onResume(); + if(!loaded && !dataLoading) + loadData(); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java new file mode 100644 index 00000000..50a71d62 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java @@ -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 onCreateRequest(String maxID, int count){ + return new GetStatusFavorites(status.id, maxID, count); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java new file mode 100644 index 00000000..e2cb0c0a --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java @@ -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 onCreateRequest(String maxID, int count){ + return new GetStatusReblogs(status.id, maxID, count); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java new file mode 100644 index 00000000..db54c485 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java @@ -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; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java new file mode 100644 index 00000000..895a0119 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java @@ -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{ + 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 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); + } + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java index badb6de9..776585cd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java @@ -29,6 +29,7 @@ import me.grishka.appkit.utils.V; public class FooterStatusDisplayItem extends StatusDisplayItem{ public final Status status; private final String accountID; + public boolean hideCounts; public FooterStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status, String accountID){ super(parentID, parentFragment); @@ -91,7 +92,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void bindButton(TextView btn, int count){ - if(count>0){ + if(count>0 && !item.hideCounts){ btn.setText(DecimalFormat.getIntegerInstance().format(count)); btn.setCompoundDrawablePadding(V.dp(8)); }else{ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index 52d58583..8feaa1dc 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -64,6 +64,7 @@ public abstract class StatusDisplayItem{ case ACCOUNT -> new AccountStatusDisplayItem.Holder(activity, parent); case HASHTAG -> new HashtagStatusDisplayItem.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, HASHTAG, - GAP + GAP, + EXTENDED_FOOTER } public static abstract class Holder extends BindableViewHolder implements UsableRecyclerView.DisableableClickable{ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 85720f6c..ab2f828b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -10,6 +10,7 @@ import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; diff --git a/mastodon/src/main/res/drawable/bg_text_button.xml b/mastodon/src/main/res/drawable/bg_text_button.xml new file mode 100644 index 00000000..f9a4ec0f --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_text_button.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/display_item_extended_footer.xml b/mastodon/src/main/res/layout/display_item_extended_footer.xml new file mode 100644 index 00000000..9f292a5e --- /dev/null +++ b/mastodon/src/main/res/layout/display_item_extended_footer.xml @@ -0,0 +1,54 @@ + + + + + +