diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3c0f0dc06..481ca2625 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -117,6 +117,11 @@
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppThemeBar"
android:label="@string/search" />
+
. */
+
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.jetbrains.annotations.NotNull;
+
+import app.fedilab.android.R;
+import app.fedilab.android.client.entities.app.Timeline;
+import app.fedilab.android.databinding.ActivityTrendsBinding;
+import app.fedilab.android.helper.Helper;
+import app.fedilab.android.helper.ThemeHelper;
+import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTag;
+import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
+
+
+public class TrendsActivity extends BaseActivity {
+
+
+ private ActivityTrendsBinding binding;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ThemeHelper.applyThemeBar(this);
+ binding = ActivityTrendsBinding.inflate(getLayoutInflater());
+ setContentView(binding.getRoot());
+
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
+ }
+
+ binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags)));
+ binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
+ binding.searchTabLayout.setTabTextColors(ThemeHelper.getAttColor(TrendsActivity.this, R.attr.mTextColor), ContextCompat.getColor(TrendsActivity.this, R.color.cyanea_accent_dark_reference));
+ binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(TrendsActivity.this));
+ binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ binding.trendsViewpager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ Fragment fragment;
+ if (binding.trendsViewpager.getAdapter() != null) {
+ fragment = (Fragment) binding.trendsViewpager.getAdapter().instantiateItem(binding.trendsViewpager, tab.getPosition());
+ if (fragment instanceof FragmentMastodonTimeline) {
+ FragmentMastodonTimeline fragmentMastodonTimeline = ((FragmentMastodonTimeline) fragment);
+ fragmentMastodonTimeline.scrollToTop();
+ } else if (fragment instanceof FragmentMastodonTag) {
+ FragmentMastodonTag fragmentMastodonTag = ((FragmentMastodonTag) fragment);
+ fragmentMastodonTag.scrollToTop();
+ }
+ }
+ }
+ });
+
+ PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
+ binding.trendsViewpager.setAdapter(mPagerAdapter);
+ binding.trendsViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ TabLayout.Tab tab = binding.searchTabLayout.getTabAt(position);
+ if (tab != null)
+ tab.select();
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+ });
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(@NotNull MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+
+ /**
+ * Pager adapter for the 4 fragments
+ */
+ @SuppressWarnings("deprecation")
+ private static class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
+ ScreenSlidePagerAdapter(FragmentManager fm) {
+ super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NotNull
+ @Override
+ public Fragment getItem(int position) {
+ Bundle bundle = new Bundle();
+ if (position == 0) {
+ FragmentMastodonTag fragmentMastodonTag = new FragmentMastodonTag();
+ bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_TAG);
+ fragmentMastodonTag.setArguments(bundle);
+ return fragmentMastodonTag;
+ }
+ FragmentMastodonTimeline fragmentMastodonTimeline = new FragmentMastodonTimeline();
+ bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TREND_MESSAGE);
+ fragmentMastodonTimeline.setArguments(bundle);
+ return fragmentMastodonTimeline;
+ }
+
+ @Override
+ public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
+ }
+
+ @Override
+ public int getCount() {
+ return 2;
+ }
+ }
+}
diff --git a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java
index 36d8c9f65..09d6da1c7 100644
--- a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java
+++ b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java
@@ -21,6 +21,7 @@ import app.fedilab.android.client.entities.api.Conversation;
import app.fedilab.android.client.entities.api.Marker;
import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.Status;
+import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.client.entities.misskey.MisskeyNote;
import app.fedilab.android.client.entities.nitter.Nitter;
import app.fedilab.android.client.entities.peertube.PeertubeVideo;
@@ -52,6 +53,14 @@ public interface MastodonTimelinesService {
@Query("limit") Integer limit
);
+
+ @GET("trends/statuses")
+ Call> getStatusTrends(@Header("Authorization") String token);
+
+
+ @GET("trends/tags")
+ Call> getTagTrends(@Header("Authorization") String token);
+
//Public Tags timelines
@GET("timelines/tag/{hashtag}")
Call> getHashTag(
diff --git a/app/src/main/java/app/fedilab/android/client/entities/app/Timeline.java b/app/src/main/java/app/fedilab/android/client/entities/app/Timeline.java
index b8a4846c0..d26badc9e 100644
--- a/app/src/main/java/app/fedilab/android/client/entities/app/Timeline.java
+++ b/app/src/main/java/app/fedilab/android/client/entities/app/Timeline.java
@@ -370,6 +370,10 @@ public class Timeline {
LIST("LIST"),
@SerializedName("REMOTE")
REMOTE("REMOTE"),
+ @SerializedName("TREND_TAG")
+ TREND_TAG("TREND_TAG"),
+ @SerializedName("TREND_MESSAGE")
+ TREND_MESSAGE("TREND_MESSAGE"),
@SerializedName("ACCOUNT_TIMELINE")
ACCOUNT_TIMELINE("ACCOUNT_TIMELINE"),
@SerializedName("MUTED_TIMELINE")
diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java
index eca471846..e287dd40e 100644
--- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java
+++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTag.java
@@ -31,12 +31,14 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Tag;
+import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.FragmentPaginationBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.TagAdapter;
import app.fedilab.android.viewmodel.mastodon.SearchVM;
+import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
public class FragmentMastodonTag extends Fragment {
@@ -45,11 +47,13 @@ public class FragmentMastodonTag extends Fragment {
private FragmentPaginationBinding binding;
private TagAdapter tagAdapter;
private String search;
+ private Timeline.TimeLineEnum timelineType;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
if (getArguments() != null) {
search = getArguments().getString(Helper.ARG_SEARCH_KEYWORD, null);
+ timelineType = (Timeline.TimeLineEnum) getArguments().get(Helper.ARG_TIMELINE_TYPE);
}
binding = FragmentPaginationBinding.inflate(inflater, container, false);
@@ -74,7 +78,7 @@ public class FragmentMastodonTag extends Fragment {
* Router for timelines
*/
private void router() {
- if (search != null) {
+ if (search != null && timelineType == null) {
SearchVM searchVM = new ViewModelProvider(FragmentMastodonTag.this).get(SearchVM.class);
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, search.trim(), null, "hashtags", false, true, false, 0, null, null, MastodonHelper.STATUSES_PER_CALL)
.observe(getViewLifecycleOwner(), results -> {
@@ -82,6 +86,12 @@ public class FragmentMastodonTag extends Fragment {
initializeTagCommonView(results.hashtags);
}
});
+ } else if (timelineType == Timeline.TimeLineEnum.TREND_TAG) {
+ TimelinesVM timelinesVM = new ViewModelProvider(FragmentMastodonTag.this).get(TimelinesVM.class);
+ timelinesVM.getTagsTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance)
+ .observe(getViewLifecycleOwner(), tags -> {
+ initializeTagCommonView(tags);
+ });
}
}
diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java
index 549124093..5d3089797 100644
--- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java
+++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java
@@ -286,11 +286,13 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
binding.loader.setVisibility(View.GONE);
binding.noAction.setVisibility(View.GONE);
binding.swipeContainer.setRefreshing(false);
- binding.swipeContainer.setOnRefreshListener(() -> {
- binding.swipeContainer.setRefreshing(true);
- flagLoading = false;
- route(DIRECTION.REFRESH, true);
- });
+ if (searchCache == null && timelineType != Timeline.TimeLineEnum.TREND_MESSAGE) {
+ binding.swipeContainer.setOnRefreshListener(() -> {
+ binding.swipeContainer.setRefreshing(true);
+ flagLoading = false;
+ route(DIRECTION.REFRESH, true);
+ });
+ }
if (statuses == null || statuses.statuses == null || statuses.statuses.size() == 0) {
binding.noAction.setVisibility(View.VISIBLE);
@@ -356,7 +358,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
binding.recyclerView.scrollToPosition(position);
}
- if (searchCache == null) {
+ if (searchCache == null && timelineType != Timeline.TimeLineEnum.TREND_MESSAGE) {
binding.recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
@@ -849,6 +851,19 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else {
flagLoading = false;
}
+ } else if (timelineType == Timeline.TimeLineEnum.TREND_MESSAGE) {
+ if (direction == null) {
+ timelinesVM.getStatusTrends(BaseMainActivity.currentToken, BaseMainActivity.currentInstance)
+ .observe(getViewLifecycleOwner(), statusesTrends -> {
+ Statuses statuses = new Statuses();
+ statuses.statuses = new ArrayList<>();
+ if (statusesTrends != null) {
+ statuses.statuses.addAll(statusesTrends);
+ }
+ statuses.pagination = new Pagination();
+ initializeStatusesCommonView(statuses);
+ });
+ }
}
};
mainHandler.post(myRunnable);
diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java
index 4ed5ad2cc..a04c2a0de 100644
--- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java
+++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java
@@ -38,6 +38,7 @@ import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.Pagination;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Statuses;
+import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.client.entities.app.StatusDraft;
@@ -75,6 +76,8 @@ public class TimelinesVM extends AndroidViewModel {
private MutableLiveData mastodonListMutableLiveData;
private MutableLiveData> mastodonListListMutableLiveData;
private MutableLiveData markerMutableLiveData;
+ private MutableLiveData> statusListMutableLiveData;
+ private MutableLiveData> tagListMutableLiveData;
public TimelinesVM(@NonNull Application application) {
super(application);
@@ -107,6 +110,56 @@ public class TimelinesVM extends AndroidViewModel {
return retrofit.create(MastodonTimelinesService.class);
}
+ public LiveData> getStatusTrends(String token, @NonNull String instance) {
+ MastodonTimelinesService mastodonTimelinesService = init(instance);
+ statusListMutableLiveData = new MutableLiveData<>();
+ new Thread(() -> {
+ Call> publicTlCall = mastodonTimelinesService.getStatusTrends(token);
+ List statusList = null;
+ if (publicTlCall != null) {
+ try {
+ Response> publicTlResponse = publicTlCall.execute();
+ if (publicTlResponse.isSuccessful()) {
+ statusList = publicTlResponse.body();
+ statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ List finalStatusList = statusList;
+ Runnable myRunnable = () -> statusListMutableLiveData.setValue(finalStatusList);
+ mainHandler.post(myRunnable);
+ }).start();
+ return statusListMutableLiveData;
+ }
+
+
+ public LiveData> getTagsTrends(String token, @NonNull String instance) {
+ MastodonTimelinesService mastodonTimelinesService = init(instance);
+ tagListMutableLiveData = new MutableLiveData<>();
+ new Thread(() -> {
+ Call> publicTlCall = mastodonTimelinesService.getTagTrends(token);
+ List tagList = null;
+ if (publicTlCall != null) {
+ try {
+ Response> publicTlResponse = publicTlCall.execute();
+ if (publicTlResponse.isSuccessful()) {
+ tagList = publicTlResponse.body();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ Handler mainHandler = new Handler(Looper.getMainLooper());
+ List finalTagList = tagList;
+ Runnable myRunnable = () -> tagListMutableLiveData.setValue(finalTagList);
+ mainHandler.post(myRunnable);
+ }).start();
+ return tagListMutableLiveData;
+ }
+
/**
* Public timeline
*
diff --git a/app/src/main/res/drawable/ic_baseline_trending_up_24.xml b/app/src/main/res/drawable/ic_baseline_trending_up_24.xml
new file mode 100644
index 000000000..04a5a8631
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_trending_up_24.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_trends.xml b/app/src/main/res/layout/activity_trends.xml
new file mode 100644
index 000000000..75ea988a0
--- /dev/null
+++ b/app/src/main/res/layout/activity_trends.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
index cd0a799fd..4ab3fc405 100644
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -35,6 +35,12 @@
android:title="@string/action_announcements"
android:visible="true" />
+
+