From 171338f492bf1c24c9eabcbd8e3e4180c72724e3 Mon Sep 17 00:00:00 2001 From: Balazs Toldi Date: Fri, 18 Aug 2023 08:55:56 +0200 Subject: [PATCH] Nicer stats for communities This commit adds more info on the community detail page. The design is heavily inspired by Memmy. Closes #45 --- .../ViewSubredditDetailActivity.java | 70 ++++++++--- .../subreddit/ParseSubredditData.java | 11 +- .../subreddit/SubredditData.java | 104 +++++++++++++++- .../community/CommunityStats.kt | 40 ++++++ app/src/main/res/drawable/ic_bolt_24.xml | 10 ++ app/src/main/res/drawable/ic_person_24.xml | 10 ++ .../activity_view_subreddit_detail.xml | 115 ++++++++++++------ .../activity_view_subreddit_detail.xml | 115 ++++++++++++------ .../layout/activity_view_subreddit_detail.xml | 115 ++++++++++++------ app/src/main/res/values/strings.xml | 5 +- 10 files changed, 453 insertions(+), 142 deletions(-) create mode 100644 app/src/main/kotlin/eu/toldi/infinityforlemmy/community/CommunityStats.kt create mode 100644 app/src/main/res/drawable/ic_bolt_24.xml create mode 100644 app/src/main/res/drawable/ic_person_24.xml diff --git a/app/src/main/java/eu/toldi/infinityforlemmy/activities/ViewSubredditDetailActivity.java b/app/src/main/java/eu/toldi/infinityforlemmy/activities/ViewSubredditDetailActivity.java index 61005448..a1ed5ab7 100644 --- a/app/src/main/java/eu/toldi/infinityforlemmy/activities/ViewSubredditDetailActivity.java +++ b/app/src/main/java/eu/toldi/infinityforlemmy/activities/ViewSubredditDetailActivity.java @@ -3,6 +3,7 @@ package eu.toldi.infinityforlemmy.activities; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.ColorStateList; +import android.graphics.PorterDuff; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -18,6 +19,7 @@ import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -36,6 +38,7 @@ import androidx.viewpager2.widget.ViewPager2; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestManager; import com.bumptech.glide.request.RequestOptions; +import com.evernote.android.state.State; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.appbar.MaterialToolbar; @@ -83,6 +86,7 @@ import eu.toldi.infinityforlemmy.bottomsheetfragments.SortTimeBottomSheetFragmen import eu.toldi.infinityforlemmy.bottomsheetfragments.SortTypeBottomSheetFragment; import eu.toldi.infinityforlemmy.bottomsheetfragments.UrlMenuBottomSheetFragment; import eu.toldi.infinityforlemmy.community.BlockCommunity; +import eu.toldi.infinityforlemmy.community.CommunityStats; import eu.toldi.infinityforlemmy.customtheme.CustomThemeWrapper; import eu.toldi.infinityforlemmy.customviews.NavigationWrapper; import eu.toldi.infinityforlemmy.customviews.slidr.Slidr; @@ -163,12 +167,23 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp TextView communityFullNameTextView; @BindView(R.id.subscriber_count_text_view_view_subreddit_detail_activity) TextView nSubscribersTextView; - @BindView(R.id.online_subscriber_count_text_view_view_subreddit_detail_activity) - TextView nOnlineSubscribersTextView; - @BindView(R.id.since_text_view_view_subreddit_detail_activity) - TextView sinceTextView; - @BindView(R.id.creation_time_text_view_view_subreddit_detail_activity) - TextView creationTimeTextView; + @BindView(R.id.active_user_count_text_view_view_subreddit_detail_activity) + TextView nActiveUsersTextView; + @BindView(R.id.post_count_text_view_view_subreddit_detail_activity) + TextView nPostsTextView; + @BindView(R.id.comment_count_text_view_view_subreddit_detail_activity) + TextView nCommentsTextView; + + @BindView(R.id.subscriber_count_image_view_view_subreddit_detail_activity) + ImageView nSubscribersImageView; + @BindView(R.id.active_user_count_image_view_view_subreddit_detail_activity) + ImageView nActiveUsersImageView; + @BindView(R.id.post_count_image_view_view_subreddit_detail_activity) + ImageView nPostsImageView; + @BindView(R.id.comment_count_image_view_view_subreddit_detail_activity) + ImageView nCommentsImageView; + + @BindView(R.id.description_text_view_view_subreddit_detail_activity) TextView descriptionTextView; @Inject @@ -212,7 +227,10 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp private int communityId; - private SubredditData communityData; + @State + SubredditData communityData; + @State + CommunityStats mCommunityStats; private String description; private String qualifiedName; @@ -363,15 +381,12 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp mMessageFullname = savedInstanceState.getInt(MESSAGE_FULLNAME_STATE); mNewAccountName = savedInstanceState.getString(NEW_ACCOUNT_NAME_STATE); - if (mFetchSubredditInfoSuccess) { - nOnlineSubscribersTextView.setText(getString(R.string.online_subscribers_number_detail, mNCurrentOnlineSubscribers)); - } } checkNewAccountAndBindView(); fetchSubredditData(); - if (communityName != null) { + if (communityData != null) { setupVisibleElements(); } } @@ -417,9 +432,13 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp subscribeSubredditChip.setTextColor(mCustomThemeWrapper.getChipTextColor()); int primaryTextColor = mCustomThemeWrapper.getPrimaryTextColor(); nSubscribersTextView.setTextColor(primaryTextColor); - nOnlineSubscribersTextView.setTextColor(primaryTextColor); - sinceTextView.setTextColor(primaryTextColor); - creationTimeTextView.setTextColor(primaryTextColor); + nActiveUsersTextView.setTextColor(primaryTextColor); + nPostsTextView.setTextColor(primaryTextColor); + nCommentsTextView.setTextColor(primaryTextColor); + nSubscribersImageView.setColorFilter(mCustomThemeWrapper.getPrimaryTextColor(), PorterDuff.Mode.SRC_IN); + nActiveUsersImageView.setColorFilter(mCustomThemeWrapper.getPrimaryTextColor(), PorterDuff.Mode.SRC_IN); + nPostsImageView.setColorFilter(mCustomThemeWrapper.getPrimaryTextColor(), PorterDuff.Mode.SRC_IN); + nCommentsImageView.setColorFilter(mCustomThemeWrapper.getPrimaryTextColor(), PorterDuff.Mode.SRC_IN); descriptionTextView.setTextColor(primaryTextColor); navigationWrapper.applyCustomTheme(mCustomThemeWrapper.getBottomAppBarIconColor(), mCustomThemeWrapper.getBottomAppBarBackgroundColor()); applyTabLayoutTheme(tabLayout); @@ -428,9 +447,9 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp subredditNameTextView.setTypeface(typeface); subscribeSubredditChip.setTypeface(typeface); nSubscribersTextView.setTypeface(typeface); - nOnlineSubscribersTextView.setTypeface(typeface); - sinceTextView.setTypeface(typeface); - creationTimeTextView.setTypeface(typeface); + nActiveUsersTextView.setTypeface(typeface); + nPostsTextView.setTypeface(typeface); + nCommentsTextView.setTypeface(typeface); descriptionTextView.setTypeface(typeface); } unsubscribedColor = mCustomThemeWrapper.getUnsubscribed(); @@ -554,7 +573,19 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp communityFullNameTextView.setText(qualifiedName); String nSubscribers = getString(R.string.subscribers_number_detail, subredditData.getNSubscribers()); nSubscribersTextView.setText(nSubscribers); - creationTimeTextView.setText(subredditData.getCreatedUTC()); + + if (mCommunityStats != null) { + nActiveUsersTextView.setText(getString(R.string.active_users_number_detail, mCommunityStats.getActiveUsers())); + nPostsTextView.setText(getString(R.string.post_count_detail, mCommunityStats.getPosts())); + nCommentsTextView.setText(getString(R.string.comment_count_detail, mCommunityStats.getComments())); + } else { + nActiveUsersTextView.setVisibility(View.GONE); + nPostsTextView.setVisibility(View.GONE); + nCommentsTextView.setVisibility(View.GONE); + nActiveUsersImageView.setVisibility(View.GONE); + nPostsImageView.setVisibility(View.GONE); + nCommentsImageView.setVisibility(View.GONE); + } description = subredditData.getDescription(); @@ -694,13 +725,14 @@ public class ViewSubredditDetailActivity extends BaseActivity implements SortTyp if (communityName == null) { communityName = communityData.getTitle(); } + mCommunityStats = communityData.getCommunityStats(); setupVisibleElements(); communityId = communityData.getId(); ViewSubredditDetailActivity.this.communityData = communityData; setupSubscribeChip(); mNCurrentOnlineSubscribers = nCurrentOnlineSubscribers; - nOnlineSubscribersTextView.setText(getString(R.string.online_subscribers_number_detail, nCurrentOnlineSubscribers)); + InsertSubredditData.insertSubredditData(mExecutor, new Handler(), mRedditDataRoomDatabase, communityData, () -> mFetchSubredditInfoSuccess = true); } diff --git a/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/ParseSubredditData.java b/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/ParseSubredditData.java index 530ce175..d1610d4f 100644 --- a/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/ParseSubredditData.java +++ b/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/ParseSubredditData.java @@ -15,6 +15,7 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import eu.toldi.infinityforlemmy.community.CommunityStats; import eu.toldi.infinityforlemmy.utils.JSONUtils; public class ParseSubredditData { @@ -71,8 +72,16 @@ public class ParseSubredditData { int instanceId = community.getInt("instance_id"); int subscribers = (subredditDataJsonObject.has("counts")) ? subredditDataJsonObject.getJSONObject("counts").getInt("subscribers") : 0; boolean blocked = (subredditDataJsonObject.has("blocked")) ? subredditDataJsonObject.getBoolean("blocked") : true; + CommunityStats stats = null; + if (subredditDataJsonObject.has("counts")) { + JSONObject counts = subredditDataJsonObject.getJSONObject("counts"); + int activeUserCount = counts.getInt("users_active_month"); + int postCount = counts.getInt("posts"); + int commentCount = counts.getInt("comments"); + stats = new CommunityStats(subscribers, activeUserCount, postCount, commentCount); + } - return new SubredditData(id, name, title, description, removed, published, updated, deleted, isNSFW, actorId, local, iconUrl, bannerImageUrl, hidden, postingRestrictedToMods, instanceId, subscribers, blocked); + return new SubredditData(id, name, title, description, removed, published, updated, deleted, isNSFW, actorId, local, iconUrl, bannerImageUrl, hidden, postingRestrictedToMods, instanceId, subscribers, blocked, stats); } interface ParseSubredditDataListener { diff --git a/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/SubredditData.java b/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/SubredditData.java index 737a09fb..c3e50b4f 100644 --- a/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/SubredditData.java +++ b/app/src/main/java/eu/toldi/infinityforlemmy/subreddit/SubredditData.java @@ -1,13 +1,18 @@ package eu.toldi.infinityforlemmy.subreddit; +import android.os.Parcel; +import android.os.Parcelable; + import androidx.annotation.NonNull; import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.Ignore; import androidx.room.PrimaryKey; +import eu.toldi.infinityforlemmy.community.CommunityStats; + @Entity(tableName = "subreddits") -public class SubredditData { +public class SubredditData implements Parcelable { @PrimaryKey @NonNull @ColumnInfo(name = "id") @@ -68,6 +73,73 @@ public class SubredditData { @Ignore private boolean isSelected; + @Ignore + private CommunityStats communityStats; + + protected SubredditData(Parcel in) { + id = in.readInt(); + name = in.readString(); + title = in.readString(); + description = in.readString(); + removed = in.readByte() != 0; + published = in.readString(); + updated = in.readString(); + deleted = in.readByte() != 0; + nsfw = in.readByte() != 0; + actorId = in.readString(); + local = in.readByte() != 0; + icon = in.readString(); + banner = in.readString(); + hidden = in.readByte() != 0; + postingRestrictedToMods = in.readByte() != 0; + instanceId = in.readInt(); + subscribers = in.readInt(); + blocked = in.readByte() != 0; + isSelected = in.readByte() != 0; + communityStats = in.readParcelable(CommunityStats.class.getClassLoader()); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(id); + dest.writeString(name); + dest.writeString(title); + dest.writeString(description); + dest.writeByte((byte) (removed ? 1 : 0)); + dest.writeString(published); + dest.writeString(updated); + dest.writeByte((byte) (deleted ? 1 : 0)); + dest.writeByte((byte) (nsfw ? 1 : 0)); + dest.writeString(actorId); + dest.writeByte((byte) (local ? 1 : 0)); + dest.writeString(icon); + dest.writeString(banner); + dest.writeByte((byte) (hidden ? 1 : 0)); + dest.writeByte((byte) (postingRestrictedToMods ? 1 : 0)); + dest.writeInt(instanceId); + dest.writeInt(subscribers); + dest.writeByte((byte) (blocked ? 1 : 0)); + dest.writeByte((byte) (isSelected ? 1 : 0)); + dest.writeParcelable(communityStats, flags); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public SubredditData createFromParcel(Parcel in) { + return new SubredditData(in); + } + + @Override + public SubredditData[] newArray(int size) { + return new SubredditData[size]; + } + }; + public int getId() { return id; } @@ -225,6 +297,28 @@ public class SubredditData { this.blocked = blocked; } + public SubredditData(int id, String name, String title, String description, boolean removed, String published, String updated, boolean deleted, boolean nsfw, String actorId, boolean local, String icon, String banner, boolean hidden, boolean postingRestrictedToMods, int instanceId, int subscribers, boolean blocked, CommunityStats communityStats) { + this.id = id; + this.name = name; + this.title = title; + this.description = description; + this.removed = removed; + this.published = published; + this.updated = updated; + this.deleted = deleted; + this.nsfw = nsfw; + this.actorId = actorId; + this.local = local; + this.icon = icon; + this.banner = banner; + this.hidden = hidden; + this.postingRestrictedToMods = postingRestrictedToMods; + this.instanceId = instanceId; + this.subscribers = subscribers; + this.blocked = blocked; + this.communityStats = communityStats; + } + public boolean isNSFW() { return nsfw; } @@ -264,4 +358,12 @@ public class SubredditData { public void setBlocked(boolean blocked) { this.blocked = blocked; } + + public CommunityStats getCommunityStats() { + return communityStats; + } + + public void setCommunityStats(CommunityStats communityStats) { + this.communityStats = communityStats; + } } diff --git a/app/src/main/kotlin/eu/toldi/infinityforlemmy/community/CommunityStats.kt b/app/src/main/kotlin/eu/toldi/infinityforlemmy/community/CommunityStats.kt new file mode 100644 index 00000000..b8d5cc9f --- /dev/null +++ b/app/src/main/kotlin/eu/toldi/infinityforlemmy/community/CommunityStats.kt @@ -0,0 +1,40 @@ +package eu.toldi.infinityforlemmy.community + +import android.os.Parcel +import android.os.Parcelable + +data class CommunityStats( + val subscribers: Int, + val activeUsers: Int, + val posts: Int, + val comments: Int +) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readInt(), + parcel.readInt(), + parcel.readInt(), + parcel.readInt() + ) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeInt(subscribers) + parcel.writeInt(activeUsers) + parcel.writeInt(posts) + parcel.writeInt(comments) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): CommunityStats { + return CommunityStats(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} diff --git a/app/src/main/res/drawable/ic_bolt_24.xml b/app/src/main/res/drawable/ic_bolt_24.xml new file mode 100644 index 00000000..449f1531 --- /dev/null +++ b/app/src/main/res/drawable/ic_bolt_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_person_24.xml b/app/src/main/res/drawable/ic_person_24.xml new file mode 100644 index 00000000..7f76fcf8 --- /dev/null +++ b/app/src/main/res/drawable/ic_person_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout-land/activity_view_subreddit_detail.xml b/app/src/main/res/layout-land/activity_view_subreddit_detail.xml index f7da6466..3dd6fa3c 100644 --- a/app/src/main/res/layout-land/activity_view_subreddit_detail.xml +++ b/app/src/main/res/layout-land/activity_view_subreddit_detail.xml @@ -81,57 +81,92 @@ android:layout_marginTop="16dp" android:layout_marginBottom="16dp"> + + - - - - - + android:orientation="vertical" + app:layout_constraintGuide_percent="0.5" /> - + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + app:layout_constraintStart_toStartOf="@+id/guideline7" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_bolt_24" /> + + + + + + + + + + diff --git a/app/src/main/res/layout-sw600dp/activity_view_subreddit_detail.xml b/app/src/main/res/layout-sw600dp/activity_view_subreddit_detail.xml index f7da6466..3dd6fa3c 100644 --- a/app/src/main/res/layout-sw600dp/activity_view_subreddit_detail.xml +++ b/app/src/main/res/layout-sw600dp/activity_view_subreddit_detail.xml @@ -81,57 +81,92 @@ android:layout_marginTop="16dp" android:layout_marginBottom="16dp"> + + - - - - - + android:orientation="vertical" + app:layout_constraintGuide_percent="0.5" /> - + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + app:layout_constraintStart_toStartOf="@+id/guideline7" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_bolt_24" /> + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_view_subreddit_detail.xml b/app/src/main/res/layout/activity_view_subreddit_detail.xml index 56c454d2..813d5926 100644 --- a/app/src/main/res/layout/activity_view_subreddit_detail.xml +++ b/app/src/main/res/layout/activity_view_subreddit_detail.xml @@ -80,57 +80,92 @@ android:layout_marginTop="16dp" android:layout_marginBottom="16dp"> + + - - - - - + android:orientation="vertical" + app:layout_constraintGuide_percent="0.5" /> - + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + app:layout_constraintStart_toStartOf="@+id/guideline7" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_bolt_24" /> + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index faa62bbc..90a27e1b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -146,7 +146,10 @@ Saved Gilded Settings - Subscribers: %1$,d + %1$,d Subscribers + %1$,d Active Users + %1$,d Posts + %1$,d Comments Online: %1$,d Cannot fetch community info Cannot fetch user info