added hashtag suggestion, renamed list

This commit is contained in:
nuclearfog 2023-10-01 21:02:48 +02:00
parent 2b6f15296c
commit 4b7c037a37
No known key found for this signature in database
GPG Key ID: 03488A185C476379
11 changed files with 107 additions and 68 deletions

View File

@ -30,7 +30,7 @@ import org.nuclearfog.twidda.model.lists.Filters;
import org.nuclearfog.twidda.model.lists.Notifications; import org.nuclearfog.twidda.model.lists.Notifications;
import org.nuclearfog.twidda.model.lists.ScheduledStatuses; import org.nuclearfog.twidda.model.lists.ScheduledStatuses;
import org.nuclearfog.twidda.model.lists.Statuses; import org.nuclearfog.twidda.model.lists.Statuses;
import org.nuclearfog.twidda.model.lists.Trends; import org.nuclearfog.twidda.model.lists.Hashtags;
import org.nuclearfog.twidda.model.lists.UserLists; import org.nuclearfog.twidda.model.lists.UserLists;
import org.nuclearfog.twidda.model.lists.Users; import org.nuclearfog.twidda.model.lists.Users;
@ -272,7 +272,7 @@ public interface Connection {
* *
* @return trend list * @return trend list
*/ */
Trends getTrends() throws ConnectionException; Hashtags getTrends() throws ConnectionException;
/** /**
* search hashtags matching search string * search hashtags matching search string
@ -280,7 +280,7 @@ public interface Connection {
* @param search text to search hashtags * @param search text to search hashtags
* @return list of trends (Hashtags) * @return list of trends (Hashtags)
*/ */
Trends searchHashtags(String search) throws ConnectionException; Hashtags searchHashtags(String search) throws ConnectionException;
/** /**
* show hashtags the current user follows them * show hashtags the current user follows them
@ -288,14 +288,21 @@ public interface Connection {
* @param cursor cursor to parse the results * @param cursor cursor to parse the results
* @return hashtag list * @return hashtag list
*/ */
Trends showHashtagFollowing(long cursor) throws ConnectionException; Hashtags showHashtagFollowing(long cursor) throws ConnectionException;
/** /**
* show featured hashtags by current user * show featured hashtags by current user
* *
* @return hashtag list * @return hashtag list
*/ */
Trends showHashtagFeaturing() throws ConnectionException; Hashtags showHashtagFeaturing() throws ConnectionException;
/**
* show suggestions to feature hashtags
*
* @return hashtag list
*/
Hashtags showHashtagSuggestions() throws ConnectionException;
/** /**
* show information of a single hashtag * show information of a single hashtag

View File

@ -60,7 +60,7 @@ import org.nuclearfog.twidda.model.lists.Filters;
import org.nuclearfog.twidda.model.lists.Notifications; import org.nuclearfog.twidda.model.lists.Notifications;
import org.nuclearfog.twidda.model.lists.ScheduledStatuses; import org.nuclearfog.twidda.model.lists.ScheduledStatuses;
import org.nuclearfog.twidda.model.lists.Statuses; import org.nuclearfog.twidda.model.lists.Statuses;
import org.nuclearfog.twidda.model.lists.Trends; import org.nuclearfog.twidda.model.lists.Hashtags;
import org.nuclearfog.twidda.model.lists.UserLists; import org.nuclearfog.twidda.model.lists.UserLists;
import org.nuclearfog.twidda.model.lists.Users; import org.nuclearfog.twidda.model.lists.Users;
@ -457,26 +457,26 @@ public class Mastodon implements Connection {
@Override @Override
public Trends getTrends() throws MastodonException { public Hashtags getTrends() throws MastodonException {
Trends result = getTrends(ENDPOINT_TRENDS, new ArrayList<>()); Hashtags result = getTrends(ENDPOINT_TRENDS, new ArrayList<>());
Collections.sort(result); Collections.sort(result);
return result; return result;
} }
@Override @Override
public Trends searchHashtags(String search) throws MastodonException { public Hashtags searchHashtags(String search) throws MastodonException {
List<String> params = new ArrayList<>(); List<String> params = new ArrayList<>();
params.add("q=" + StringUtils.encode(search)); params.add("q=" + StringUtils.encode(search));
params.add("type=hashtags"); params.add("type=hashtags");
Trends result = getTrends(ENDPOINT_SEARCH_TIMELINE, params); Hashtags result = getTrends(ENDPOINT_SEARCH_TIMELINE, params);
Collections.sort(result); Collections.sort(result);
return result; return result;
} }
@Override @Override
public Trends showHashtagFollowing(long cursor) throws ConnectionException { public Hashtags showHashtagFollowing(long cursor) throws ConnectionException {
List<String> params = new ArrayList<>(); List<String> params = new ArrayList<>();
if (cursor != 0L) if (cursor != 0L)
params.add("max_id=" + cursor); params.add("max_id=" + cursor);
@ -485,11 +485,17 @@ public class Mastodon implements Connection {
@Override @Override
public Trends showHashtagFeaturing() throws ConnectionException { public Hashtags showHashtagFeaturing() throws ConnectionException {
return getTrends(ENDPOINT_HASHTAG_FEATURE, new ArrayList<>()); return getTrends(ENDPOINT_HASHTAG_FEATURE, new ArrayList<>());
} }
@Override
public Hashtags showHashtagSuggestions() throws ConnectionException {
return getTrends(ENDPOINT_HASHTAG_FEATURE + "/suggestions", new ArrayList<>());
}
@Override @Override
public Hashtag showHashtag(String name) throws ConnectionException { public Hashtag showHashtag(String name) throws ConnectionException {
try { try {
@ -1434,7 +1440,7 @@ public class Mastodon implements Connection {
* @param params additional parameters * @param params additional parameters
* @return trend list * @return trend list
*/ */
private Trends getTrends(String endpoint, List<String> params) throws MastodonException { private Hashtags getTrends(String endpoint, List<String> params) throws MastodonException {
try { try {
params.add("limit=" + settings.getListSize()); params.add("limit=" + settings.getListSize());
Response response = get(endpoint, params); Response response = get(endpoint, params);
@ -1448,7 +1454,7 @@ public class Mastodon implements Connection {
jsonArray = new JSONObject(jsonStr).getJSONArray("hashtags"); jsonArray = new JSONObject(jsonStr).getJSONArray("hashtags");
} }
long[] cursors = getCursors(response); long[] cursors = getCursors(response);
Trends result = new Trends(cursors[0], cursors[1]); Hashtags result = new Hashtags(cursors[0], cursors[1]);
for (int i = 0; i < jsonArray.length(); i++) { for (int i = 0; i < jsonArray.length(); i++) {
MastodonHashtag item = new MastodonHashtag(jsonArray.getJSONObject(i)); MastodonHashtag item = new MastodonHashtag(jsonArray.getJSONObject(i));
item.setRank(i + 1); item.setRank(i + 1);

View File

@ -9,7 +9,7 @@ import org.nuclearfog.twidda.backend.api.Connection;
import org.nuclearfog.twidda.backend.api.ConnectionException; import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.api.ConnectionManager; import org.nuclearfog.twidda.backend.api.ConnectionManager;
import org.nuclearfog.twidda.database.AppDatabase; import org.nuclearfog.twidda.database.AppDatabase;
import org.nuclearfog.twidda.model.lists.Trends; import org.nuclearfog.twidda.model.lists.Hashtags;
import org.nuclearfog.twidda.ui.fragments.HashtagFragment; import org.nuclearfog.twidda.ui.fragments.HashtagFragment;
/** /**
@ -37,28 +37,32 @@ public class HashtagLoader extends AsyncExecutor<HashtagLoader.Param, HashtagLoa
try { try {
switch (param.mode) { switch (param.mode) {
case Param.POPULAR_OFFLINE: case Param.POPULAR_OFFLINE:
Trends trends = db.getTrends(); Hashtags hashtags = db.getTrends();
if (!trends.isEmpty()) { if (!hashtags.isEmpty()) {
return new Result(Result.POPULAR, trends, param.index, null); return new Result(Result.POPULAR, hashtags, param.index, null);
} }
// fall through // fall through
case Param.POPULAR_ONLINE: case Param.POPULAR_ONLINE:
trends = connection.getTrends(); hashtags = connection.getTrends();
db.saveTrends(trends); db.saveTrends(hashtags);
return new Result(Result.POPULAR, trends, param.index, null); return new Result(Result.POPULAR, hashtags, param.index, null);
case Param.SEARCH: case Param.SEARCH:
trends = connection.searchHashtags(param.trend); hashtags = connection.searchHashtags(param.trend);
return new Result(Result.SEARCH, trends, param.index, null); return new Result(Result.SEARCH, hashtags, param.index, null);
case Param.FOLLOWING: case Param.FOLLOWING:
trends = connection.showHashtagFollowing(param.cursor); hashtags = connection.showHashtagFollowing(param.cursor);
return new Result(Result.FOLLOWING, trends, param.index, null); return new Result(Result.FOLLOWING, hashtags, param.index, null);
case Param.FEATURING: case Param.FEATURING:
trends = connection.showHashtagFeaturing(); hashtags = connection.showHashtagFeaturing();
return new Result(Result.FEATURING, trends, param.index, null); return new Result(Result.FEATURING, hashtags, param.index, null);
case Param.SUGGESTIONS:
hashtags = connection.showHashtagSuggestions();
return new Result(Result.SUGGESTIONS, hashtags, param.index, null);
default: default:
return null; return null;
@ -78,6 +82,7 @@ public class HashtagLoader extends AsyncExecutor<HashtagLoader.Param, HashtagLoa
public static final int SEARCH = 3; public static final int SEARCH = 3;
public static final int FOLLOWING = 4; public static final int FOLLOWING = 4;
public static final int FEATURING = 5; public static final int FEATURING = 5;
public static final int SUGGESTIONS = 6;
public static final long NO_CURSOR = 0L; public static final long NO_CURSOR = 0L;
@ -104,16 +109,17 @@ public class HashtagLoader extends AsyncExecutor<HashtagLoader.Param, HashtagLoa
public static final int SEARCH = 21; public static final int SEARCH = 21;
public static final int FOLLOWING = 22; public static final int FOLLOWING = 22;
public static final int FEATURING = 23; public static final int FEATURING = 23;
public static final int SUGGESTIONS = 24;
public final int mode; public final int mode;
public final int index; public final int index;
@Nullable @Nullable
public final Trends trends; public final Hashtags hashtags;
@Nullable @Nullable
public final ConnectionException exception; public final ConnectionException exception;
Result(int mode, @Nullable Trends trends, int index, @Nullable ConnectionException exception) { Result(int mode, @Nullable Hashtags hashtags, int index, @Nullable ConnectionException exception) {
this.trends = trends; this.hashtags = hashtags;
this.exception = exception; this.exception = exception;
this.index = index; this.index = index;
this.mode = mode; this.mode = mode;

View File

@ -45,7 +45,7 @@ import org.nuclearfog.twidda.model.User;
import org.nuclearfog.twidda.model.lists.Accounts; import org.nuclearfog.twidda.model.lists.Accounts;
import org.nuclearfog.twidda.model.lists.Notifications; import org.nuclearfog.twidda.model.lists.Notifications;
import org.nuclearfog.twidda.model.lists.Statuses; import org.nuclearfog.twidda.model.lists.Statuses;
import org.nuclearfog.twidda.model.lists.Trends; import org.nuclearfog.twidda.model.lists.Hashtags;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -776,20 +776,20 @@ public class AppDatabase {
* *
* @return list of trends * @return list of trends
*/ */
public Trends getTrends() { public Hashtags getTrends() {
synchronized (LOCK) { synchronized (LOCK) {
String[] args = {Long.toString(settings.getTrendLocation().getId())}; String[] args = {Long.toString(settings.getTrendLocation().getId())};
SQLiteDatabase db = adapter.getDbRead(); SQLiteDatabase db = adapter.getDbRead();
Cursor cursor = db.query(HashtagTable.NAME, DatabaseHashtag.COLUMNS, TREND_SELECT, args, null, null, null); Cursor cursor = db.query(HashtagTable.NAME, DatabaseHashtag.COLUMNS, TREND_SELECT, args, null, null, null);
Trends trends = new Trends(); Hashtags hashtags = new Hashtags();
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
do { do {
trends.add(new DatabaseHashtag(cursor)); hashtags.add(new DatabaseHashtag(cursor));
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
cursor.close(); cursor.close();
Collections.sort(trends); Collections.sort(hashtags);
return trends; return hashtags;
} }
} }

View File

@ -8,11 +8,11 @@ import org.nuclearfog.twidda.model.Hashtag;
import java.util.LinkedList; import java.util.LinkedList;
/** /**
* Trend list implementation with addtitional paging IDs * Hashtag list implementation with addtitional paging cursors
* *
* @author nuclearfog * @author nuclearfog
*/ */
public class Trends extends LinkedList<Hashtag> { public class Hashtags extends LinkedList<Hashtag> {
private static final long serialVersionUID = 7646437787602696292L; private static final long serialVersionUID = 7646437787602696292L;
@ -21,7 +21,7 @@ public class Trends extends LinkedList<Hashtag> {
/** /**
* *
*/ */
public Trends() { public Hashtags() {
this(0L, 0L); this(0L, 0L);
} }
@ -29,19 +29,19 @@ public class Trends extends LinkedList<Hashtag> {
* @param prevCursor minimum ID of an item * @param prevCursor minimum ID of an item
* @param nextCursor maximum ID of an item * @param nextCursor maximum ID of an item
*/ */
public Trends(long prevCursor, long nextCursor) { public Hashtags(long prevCursor, long nextCursor) {
super(); super();
this.nextCursor = nextCursor; this.nextCursor = nextCursor;
this.prevCursor = prevCursor; this.prevCursor = prevCursor;
} }
/** /**
* @param trends trend list to clone * @param hashtags trend list to clone
*/ */
public Trends(Trends trends) { public Hashtags(Hashtags hashtags) {
super(trends); super(hashtags);
prevCursor = trends.prevCursor; prevCursor = hashtags.prevCursor;
nextCursor = trends.nextCursor; nextCursor = hashtags.nextCursor;
} }
/** /**
@ -70,31 +70,31 @@ public class Trends extends LinkedList<Hashtag> {
/** /**
* add a sublist at specific position * add a sublist at specific position
* *
* @param trends sublist to add * @param hashtags sublist to add
* @param index index where to insert the sublist * @param index index where to insert the sublist
*/ */
public void addAll(int index, Trends trends) { public void addAll(int index, Hashtags hashtags) {
if (isEmpty()) { if (isEmpty()) {
prevCursor = trends.getPreviousCursor(); prevCursor = hashtags.getPreviousCursor();
nextCursor = trends.getNextCursor(); nextCursor = hashtags.getNextCursor();
} else if (index == 0) { } else if (index == 0) {
prevCursor = trends.getPreviousCursor(); prevCursor = hashtags.getPreviousCursor();
} else if (index == size() - 1) { } else if (index == size() - 1) {
nextCursor = trends.getNextCursor(); nextCursor = hashtags.getNextCursor();
} }
super.addAll(index, trends); super.addAll(index, hashtags);
} }
/** /**
* replace all items with new ones * replace all items with new ones
* *
* @param trends new items to insert * @param hashtags new items to insert
*/ */
public void replaceAll(Trends trends) { public void replaceAll(Hashtags hashtags) {
clear(); clear();
addAll(trends); addAll(hashtags);
prevCursor = trends.getPreviousCursor(); prevCursor = hashtags.getPreviousCursor();
nextCursor = trends.getNextCursor(); nextCursor = hashtags.getNextCursor();
} }

View File

@ -52,7 +52,7 @@ public class HashtagActivity extends AppCompatActivity implements SearchView.OnQ
settings = GlobalSettings.get(this); settings = GlobalSettings.get(this);
adapter = new HashtagAdapter(this); adapter = new HashtagAdapter(this);
viewPager.setAdapter(adapter); viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(2); viewPager.setOffscreenPageLimit(3);
tabSelector.addTabIcons(R.array.userlist_hashtag_icons); tabSelector.addTabIcons(R.array.userlist_hashtag_icons);
tabSelector.addTabLabels(R.array.hashtag_labels); tabSelector.addTabLabels(R.array.hashtag_labels);

View File

@ -7,7 +7,7 @@ import androidx.recyclerview.widget.RecyclerView.Adapter;
import androidx.recyclerview.widget.RecyclerView.ViewHolder; import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.nuclearfog.twidda.model.Hashtag; import org.nuclearfog.twidda.model.Hashtag;
import org.nuclearfog.twidda.model.lists.Trends; import org.nuclearfog.twidda.model.lists.Hashtags;
import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.HashtagHolder; import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.HashtagHolder;
import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.OnHolderClickListener; import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.OnHolderClickListener;
import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.PlaceHolder; import org.nuclearfog.twidda.ui.adapter.recyclerview.holder.PlaceHolder;
@ -33,7 +33,7 @@ public class HashtagAdapter extends Adapter<ViewHolder> implements OnHolderClick
private OnHashtagClickListener itemClickListener; private OnHashtagClickListener itemClickListener;
private Trends items = new Trends(); private Hashtags items = new Hashtags();
private int loadingIndex = NO_LOADING; private int loadingIndex = NO_LOADING;
private boolean enableDelete = false; private boolean enableDelete = false;
@ -109,8 +109,8 @@ public class HashtagAdapter extends Adapter<ViewHolder> implements OnHolderClick
* *
* @return a copy of the items * @return a copy of the items
*/ */
public Trends getItems() { public Hashtags getItems() {
return new Trends(items); return new Hashtags(items);
} }
/** /**
@ -118,7 +118,7 @@ public class HashtagAdapter extends Adapter<ViewHolder> implements OnHolderClick
* *
* @param newItems array of trend items * @param newItems array of trend items
*/ */
public void addItems(Trends newItems, int index) { public void addItems(Hashtags newItems, int index) {
disableLoading(); disableLoading();
if (index < 0) { if (index < 0) {
this.items.replaceAll(newItems); this.items.replaceAll(newItems);

View File

@ -30,7 +30,13 @@ public class HashtagAdapter extends ViewPagerAdapter {
paramFeaturedTags.putInt(HashtagFragment.KEY_MODE, HashtagFragment.MODE_FEATURE); paramFeaturedTags.putInt(HashtagFragment.KEY_MODE, HashtagFragment.MODE_FEATURE);
featuredTags.setArguments(paramFeaturedTags); featuredTags.setArguments(paramFeaturedTags);
HashtagFragment suggestedTags = new HashtagFragment();
Bundle paramSuggestedTags = new Bundle();
paramSuggestedTags.putInt(HashtagFragment.KEY_MODE, HashtagFragment.MODE_SUGGESTIONS);
suggestedTags.setArguments(paramSuggestedTags);
fragments.add(followedTags); fragments.add(followedTags);
fragments.add(featuredTags); fragments.add(featuredTags);
fragments.add(suggestedTags);
} }
} }

View File

@ -18,7 +18,7 @@ import org.nuclearfog.twidda.backend.async.HashtagAction;
import org.nuclearfog.twidda.backend.async.HashtagLoader; import org.nuclearfog.twidda.backend.async.HashtagLoader;
import org.nuclearfog.twidda.backend.utils.ErrorUtils; import org.nuclearfog.twidda.backend.utils.ErrorUtils;
import org.nuclearfog.twidda.model.Hashtag; import org.nuclearfog.twidda.model.Hashtag;
import org.nuclearfog.twidda.model.lists.Trends; import org.nuclearfog.twidda.model.lists.Hashtags;
import org.nuclearfog.twidda.ui.activities.SearchActivity; import org.nuclearfog.twidda.ui.activities.SearchActivity;
import org.nuclearfog.twidda.ui.adapter.recyclerview.HashtagAdapter; import org.nuclearfog.twidda.ui.adapter.recyclerview.HashtagAdapter;
import org.nuclearfog.twidda.ui.adapter.recyclerview.HashtagAdapter.OnHashtagClickListener; import org.nuclearfog.twidda.ui.adapter.recyclerview.HashtagAdapter.OnHashtagClickListener;
@ -54,6 +54,11 @@ public class HashtagFragment extends ListFragment implements OnHashtagClickListe
*/ */
public static final int MODE_FEATURE = 0x16347583; public static final int MODE_FEATURE = 0x16347583;
/**
* setup fragment to view suggestions for hashtag features
*/
public static final int MODE_SUGGESTIONS = 0x4422755;
/** /**
* key used to define what type of trends should be shown, see {@link #MODE_FOLLOW ,#MODE_POPULAR ,#KEY_FRAGMENT_TREND_SEARCH} * key used to define what type of trends should be shown, see {@link #MODE_FOLLOW ,#MODE_POPULAR ,#KEY_FRAGMENT_TREND_SEARCH}
* value type is Integer * value type is Integer
@ -68,7 +73,7 @@ public class HashtagFragment extends ListFragment implements OnHashtagClickListe
/** /**
* bundle key to add adapter items * bundle key to add adapter items
* value type is {@link Trends} * value type is {@link Hashtags}
*/ */
private static final String KEY_DATA = "fragment_trend_data"; private static final String KEY_DATA = "fragment_trend_data";
@ -106,8 +111,8 @@ public class HashtagFragment extends ListFragment implements OnHashtagClickListe
} }
if (savedInstanceState != null) { if (savedInstanceState != null) {
Serializable data = savedInstanceState.getSerializable(KEY_DATA); Serializable data = savedInstanceState.getSerializable(KEY_DATA);
if (data instanceof Trends) { if (data instanceof Hashtags) {
adapter.addItems((Trends) data, HashtagAdapter.CLEAR_LIST); adapter.addItems((Hashtags) data, HashtagAdapter.CLEAR_LIST);
return; return;
} }
} }
@ -237,7 +242,7 @@ public class HashtagFragment extends ListFragment implements OnHashtagClickListe
} }
adapter.disableLoading(); adapter.disableLoading();
} else { } else {
adapter.addItems(result.trends, result.index); adapter.addItems(result.hashtags, result.index);
} }
setRefresh(false); setRefresh(false);
} }
@ -271,6 +276,12 @@ public class HashtagFragment extends ListFragment implements OnHashtagClickListe
param = new HashtagLoader.Param(HashtagLoader.Param.SEARCH, index, search, cursor); param = new HashtagLoader.Param(HashtagLoader.Param.SEARCH, index, search, cursor);
hashtagLoader.execute(param, hashtagLoaderCallback); hashtagLoader.execute(param, hashtagLoaderCallback);
break; break;
case MODE_SUGGESTIONS:
param = new HashtagLoader.Param(HashtagLoader.Param.SUGGESTIONS, index, search, cursor);
hashtagLoader.execute(param, hashtagLoaderCallback);
break;
} }
} }
} }

View File

@ -38,6 +38,7 @@
<string-array name="hashtag_labels"> <string-array name="hashtag_labels">
<item>gefolgt</item> <item>gefolgt</item>
<item>vorgestellt</item> <item>vorgestellt</item>
<item>voegeschlagen</item>
</string-array> </string-array>
<string-array name="userlist_policy"> <string-array name="userlist_policy">

View File

@ -72,6 +72,7 @@
<integer-array name="userlist_hashtag_icons"> <integer-array name="userlist_hashtag_icons">
<item>@drawable/hash</item> <item>@drawable/hash</item>
<item>@drawable/hash</item> <item>@drawable/hash</item>
<item>@drawable/hash</item>
</integer-array> </integer-array>
<string-array name="networks"> <string-array name="networks">
@ -115,6 +116,7 @@
<string-array name="hashtag_labels"> <string-array name="hashtag_labels">
<item>followed</item> <item>followed</item>
<item>featured</item> <item>featured</item>
<item>suggested</item>
</string-array> </string-array>
<string-array name="userlist_policy"> <string-array name="userlist_policy">