From fed377cec01f78b701b92a6bf6b8e5bc82cb37b1 Mon Sep 17 00:00:00 2001 From: tom79 Date: Sat, 30 Nov 2019 15:47:21 +0100 Subject: [PATCH] Add backend --- .../android/activities/BaseMainActivity.java | 7 + .../android/activities/HashTagActivity.java | 6 +- .../activities/SearchResultActivity.java | 37 ++-- .../asynctasks/RetrieveSearchAsyncTask.java | 4 +- .../java/app/fedilab/android/client/API.java | 87 ++++++++- .../fedilab/android/client/APIResponse.java | 10 + .../android/client/Entities/Trends.java | 55 ++++++ .../client/Entities/TrendsHistory.java | 52 ++++++ .../android/drawers/SearchListAdapter.java | 7 +- .../android/drawers/TrendsAdapter.java | 176 ++++++++++++++++++ app/src/main/res/layout/drawer_tag_trends.xml | 60 ++++++ app/src/main/res/values/strings.xml | 2 + 12 files changed, 477 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/client/Entities/Trends.java create mode 100644 app/src/main/java/app/fedilab/android/client/Entities/TrendsHistory.java create mode 100644 app/src/main/java/app/fedilab/android/drawers/TrendsAdapter.java create mode 100644 app/src/main/res/layout/drawer_tag_trends.xml diff --git a/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java index b1efb8ece..417c3a14d 100644 --- a/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/BaseMainActivity.java @@ -1766,6 +1766,13 @@ public abstract class BaseMainActivity extends BaseActivity Intent myIntent = new Intent(BaseMainActivity.this, OwnerStatusActivity.class); startActivity(myIntent); return false; + }if (id == R.id.nav_trends) { + Intent myIntent = new Intent(BaseMainActivity.this, SearchResultActivity.class); + Bundle b = new Bundle(); + b.putString("search", "fedilab_trend"); + myIntent.putExtras(b); + startActivity(myIntent); + return false; } else if (id == R.id.nav_archive_notifications) { Intent myIntent = new Intent(BaseMainActivity.this, OwnerNotificationActivity.class); startActivity(myIntent); diff --git a/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java b/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java index 5b12cf37e..582ca3d1a 100644 --- a/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/HashTagActivity.java @@ -79,9 +79,6 @@ public class HashTagActivity extends BaseActivity implements OnRetrieveFeedsInte case Helper.THEME_LIGHT: setTheme(R.style.AppTheme_NoActionBar_Fedilab); break; - case Helper.THEME_DARK: - setTheme(R.style.AppThemeDark_NoActionBar); - break; case Helper.THEME_BLACK: setTheme(R.style.AppThemeBlack_NoActionBar); break; @@ -96,8 +93,9 @@ public class HashTagActivity extends BaseActivity implements OnRetrieveFeedsInte if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); Bundle b = getIntent().getExtras(); - if (b != null) + if (b != null) { tag = b.getString("tag", null); + } if (tag == null) finish(); statuses = new ArrayList<>(); diff --git a/app/src/main/java/app/fedilab/android/activities/SearchResultActivity.java b/app/src/main/java/app/fedilab/android/activities/SearchResultActivity.java index 8dd92aa1b..ca9c957ed 100644 --- a/app/src/main/java/app/fedilab/android/activities/SearchResultActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/SearchResultActivity.java @@ -41,7 +41,9 @@ import app.fedilab.android.client.APIResponse; import app.fedilab.android.client.Entities.Account; import app.fedilab.android.client.Entities.Error; import app.fedilab.android.client.Entities.Status; +import app.fedilab.android.client.Entities.Trends; import app.fedilab.android.drawers.SearchListAdapter; +import app.fedilab.android.drawers.TrendsAdapter; import app.fedilab.android.helper.Helper; import app.fedilab.android.interfaces.OnRetrieveSearchInterface; import app.fedilab.android.interfaces.OnRetrieveSearchStatusInterface; @@ -59,6 +61,7 @@ public class SearchResultActivity extends BaseActivity implements OnRetrieveSear private String search; private ListView lv_search; private RelativeLayout loader; + private boolean forTrends; @Override protected void onCreate(Bundle savedInstanceState) { @@ -69,16 +72,13 @@ public class SearchResultActivity extends BaseActivity implements OnRetrieveSear case Helper.THEME_LIGHT: setTheme(R.style.AppTheme_Fedilab); break; - case Helper.THEME_DARK: - setTheme(R.style.AppThemeDark); - break; case Helper.THEME_BLACK: setTheme(R.style.AppThemeBlack); break; default: setTheme(R.style.AppThemeDark); } - + forTrends = false; setContentView(R.layout.activity_search_result); loader = findViewById(R.id.loader); @@ -94,6 +94,9 @@ public class SearchResultActivity extends BaseActivity implements OnRetrieveSear } else { Toasty.error(getApplicationContext(), getString(R.string.toast_error_search), Toast.LENGTH_LONG).show(); } + if( search.compareTo("fedilab_trend") == 0 ) { + forTrends = true; + } if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); ActionBar actionBar = getSupportActionBar(); @@ -114,7 +117,11 @@ public class SearchResultActivity extends BaseActivity implements OnRetrieveSear }); toolbar_title.setText(search); } - setTitle(search); + if( !forTrends) { + setTitle(search); + }else{ + setTitle(R.string.trending_now); + } loader.setVisibility(View.VISIBLE); lv_search.setVisibility(View.GONE); @@ -153,14 +160,20 @@ public class SearchResultActivity extends BaseActivity implements OnRetrieveSear return; } lv_search.setVisibility(View.VISIBLE); - List tags = apiResponse.getResults().getHashtags(); - List accounts = apiResponse.getResults().getAccounts(); - List statuses = apiResponse.getResults().getStatuses(); - - SearchListAdapter searchListAdapter = new SearchListAdapter(SearchResultActivity.this, statuses, accounts, tags); - lv_search.setAdapter(searchListAdapter); - searchListAdapter.notifyDataSetChanged(); + if (!forTrends) { + List tags = apiResponse.getResults().getHashtags(); + List accounts = apiResponse.getResults().getAccounts(); + List statuses = apiResponse.getResults().getStatuses(); + SearchListAdapter searchListAdapter = new SearchListAdapter(SearchResultActivity.this, statuses, accounts, tags); + lv_search.setAdapter(searchListAdapter); + searchListAdapter.notifyDataSetChanged(); + } else { + List trends = apiResponse.getTrends(); + TrendsAdapter trendsAdapter = new TrendsAdapter(SearchResultActivity.this, trends); + lv_search.setAdapter(trendsAdapter); + trendsAdapter.notifyDataSetChanged(); + } } diff --git a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveSearchAsyncTask.java b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveSearchAsyncTask.java index 1bcccf52c..5e518490f 100644 --- a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveSearchAsyncTask.java +++ b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveSearchAsyncTask.java @@ -72,7 +72,9 @@ public class RetrieveSearchAsyncTask extends AsyncTask { @Override protected Void doInBackground(Void... params) { - if (this.type == null) { + if( query.compareTo("fedilab_trend") == 0 ) { + apiResponse = new API(this.contextReference.get()).getTrends(); + }else if (this.type == null) { if (MainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.FRIENDICA) { API api = new API(this.contextReference.get()); String[] split = query.trim().split("@"); diff --git a/app/src/main/java/app/fedilab/android/client/API.java b/app/src/main/java/app/fedilab/android/client/API.java index 36050416c..0eb97afd4 100644 --- a/app/src/main/java/app/fedilab/android/client/API.java +++ b/app/src/main/java/app/fedilab/android/client/API.java @@ -78,6 +78,8 @@ import app.fedilab.android.client.Entities.Schedule; import app.fedilab.android.client.Entities.Status; import app.fedilab.android.client.Entities.StoredStatus; import app.fedilab.android.client.Entities.Tag; +import app.fedilab.android.client.Entities.Trends; +import app.fedilab.android.client.Entities.TrendsHistory; import app.fedilab.android.fragments.DisplayNotificationsFragment; import app.fedilab.android.helper.Helper; import app.fedilab.android.sqlite.AccountDAO; @@ -4711,13 +4713,31 @@ public class API { } catch (HttpsConnection.HttpsConnectionException e) { setError(e.getStatusCode(), e); e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { e.printStackTrace(); - } catch (IOException e) { + } + return apiResponse; + } + + + /** + * Retrieves trends for Mastodon *synchronously* + * + * @return APIResponse + */ + public APIResponse getTrends() { + + List trends = new ArrayList<>(); + apiResponse = new APIResponse(); + try { + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get(getAbsoluteUr2l("/trends"), 10, null, prefKeyOauthTokenT); + trends = parseTrends(new JSONArray(response)); + apiResponse.setTrends(trends); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); e.printStackTrace(); - } catch (KeyManagementException e) { - e.printStackTrace(); - } catch (JSONException e) { + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { e.printStackTrace(); } return apiResponse; @@ -5559,6 +5579,63 @@ public class API { return peertubes; } + + /** + * Parse json response for several trends + * + * @param jsonArray JSONArray + * @return List + */ + private List parseTrends(JSONArray jsonArray) { + + List trends = new ArrayList<>(); + try { + int i = 0; + while (i < jsonArray.length()) { + + JSONObject resobj = jsonArray.getJSONObject(i); + Trends trend = parseTrends(resobj); + i++; + trends.add(trend); + } + + } catch (JSONException e) { + setDefaultError(e); + } + return trends; + } + + /** + * Parse json response for unique trend + * + * @param resobj JSONObject + * @return Trend + */ + private Trends parseTrends(JSONObject resobj) { + Trends trend = new Trends(); + try { + trend.setName(resobj.getString("name")); + trend.setUrl(resobj.getString("url")); + List historyList = new ArrayList<>(); + if( resobj.has("history")) { + JSONArray histories = resobj.getJSONArray("history"); + for(int i = 0 ; i < histories.length() ; i++ ) { + JSONObject hystory = histories.getJSONObject(i); + TrendsHistory trendsHistory = new TrendsHistory(); + trendsHistory.setDays(hystory.getLong("day")); + trendsHistory.setUses(hystory.getInt("uses")); + trendsHistory.setAccounts(hystory.getInt("accounts")); + historyList.add(trendsHistory); + } + } + trend.setTrendsHistory(historyList); + } catch (JSONException ignored) { + } + return trend; + } + + + /** * Parse json response for several conversations * diff --git a/app/src/main/java/app/fedilab/android/client/APIResponse.java b/app/src/main/java/app/fedilab/android/client/APIResponse.java index efdea29e6..feecb07ad 100644 --- a/app/src/main/java/app/fedilab/android/client/APIResponse.java +++ b/app/src/main/java/app/fedilab/android/client/APIResponse.java @@ -37,6 +37,7 @@ import app.fedilab.android.client.Entities.Report; import app.fedilab.android.client.Entities.Results; import app.fedilab.android.client.Entities.Status; import app.fedilab.android.client.Entities.StoredStatus; +import app.fedilab.android.client.Entities.Trends; /** * Created by Thomas on 03/06/2017. @@ -71,6 +72,7 @@ public class APIResponse { private List reports = null; private Context context = null; private PixelFedStory pixelFedStory = null; + private List trends = null; public List getAccounts() { return accounts; @@ -287,4 +289,12 @@ public class APIResponse { public void setPixelFedStory(PixelFedStory pixelFedStory) { this.pixelFedStory = pixelFedStory; } + + public List getTrends() { + return trends; + } + + public void setTrends(List trends) { + this.trends = trends; + } } diff --git a/app/src/main/java/app/fedilab/android/client/Entities/Trends.java b/app/src/main/java/app/fedilab/android/client/Entities/Trends.java new file mode 100644 index 000000000..2ec9baec0 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/client/Entities/Trends.java @@ -0,0 +1,55 @@ +package app.fedilab.android.client.Entities; +/* Copyright 2019 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + + +import java.util.List; + +/** + * Created by Thomas on 30/11/2019. + * Manage Tag trends + */ + +public class Trends { + + private String name; + private String url; + private List trendsHistory; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + + public List getTrendsHistory() { + return trendsHistory; + } + + public void setTrendsHistory(List trendsHistory) { + this.trendsHistory = trendsHistory; + } +} diff --git a/app/src/main/java/app/fedilab/android/client/Entities/TrendsHistory.java b/app/src/main/java/app/fedilab/android/client/Entities/TrendsHistory.java new file mode 100644 index 000000000..5cabf3325 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/client/Entities/TrendsHistory.java @@ -0,0 +1,52 @@ +package app.fedilab.android.client.Entities; +/* Copyright 2019 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + + +/** + * Created by Thomas on 30/11/2019. + * Manage Tag trends history + */ + +public class TrendsHistory { + + private long days; + private int uses; + private int accounts; + + public long getDays() { + return days; + } + + public void setDays(long days) { + this.days = days; + } + + public int getUses() { + return uses; + } + + public void setUses(int uses) { + this.uses = uses; + } + + public int getAccounts() { + return accounts; + } + + public void setAccounts(int accounts) { + this.accounts = accounts; + } +} diff --git a/app/src/main/java/app/fedilab/android/drawers/SearchListAdapter.java b/app/src/main/java/app/fedilab/android/drawers/SearchListAdapter.java index 0dd38c406..349717cdf 100644 --- a/app/src/main/java/app/fedilab/android/drawers/SearchListAdapter.java +++ b/app/src/main/java/app/fedilab/android/drawers/SearchListAdapter.java @@ -63,9 +63,9 @@ public class SearchListAdapter extends BaseAdapter { public SearchListAdapter(Context context, List statuses, List accounts, List tags) { this.context = context; - this.statuses = (statuses != null) ? statuses : new ArrayList(); - this.accounts = (accounts != null) ? accounts : new ArrayList(); - this.tags = (tags != null) ? tags : new ArrayList(); + this.statuses = (statuses != null) ? statuses : new ArrayList<>(); + this.accounts = (accounts != null) ? accounts : new ArrayList<>(); + this.tags = (tags != null) ? tags : new ArrayList<>(); layoutInflater = LayoutInflater.from(context); } @@ -180,7 +180,6 @@ public class SearchListAdapter extends BaseAdapter { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) holder.status_content.setText(Html.fromHtml(content, Html.FROM_HTML_MODE_LEGACY)); else - //noinspection deprecation holder.status_content.setText(Html.fromHtml(content)); holder.status_content.setAutoLinkMask(Linkify.WEB_URLS); holder.status_toot_date.setText(Helper.dateDiff(context, status.getCreated_at())); diff --git a/app/src/main/java/app/fedilab/android/drawers/TrendsAdapter.java b/app/src/main/java/app/fedilab/android/drawers/TrendsAdapter.java new file mode 100644 index 000000000..1d103c92c --- /dev/null +++ b/app/src/main/java/app/fedilab/android/drawers/TrendsAdapter.java @@ -0,0 +1,176 @@ +package app.fedilab.android.drawers; +/* Copyright 2019 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ + +import android.content.Context; +import android.content.Intent; +import android.graphics.Paint; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import androidx.core.content.ContextCompat; + +import com.github.mikephil.charting.charts.LineChart; +import com.github.mikephil.charting.components.XAxis; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.LineData; +import com.github.mikephil.charting.data.LineDataSet; +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import app.fedilab.android.R; +import app.fedilab.android.activities.HashTagActivity; +import app.fedilab.android.client.Entities.Trends; +import app.fedilab.android.client.Entities.TrendsHistory; + + +/** + * Created by Thomas on 30/11/2019. + * Adapter for Trends results + */ +public class TrendsAdapter extends BaseAdapter { + + private List trends; + private Context context; + private LayoutInflater layoutInflater; + + public TrendsAdapter(Context context, List trends) { + this.context = context; + this.trends = (trends != null) ? trends : new ArrayList<>(); + layoutInflater = LayoutInflater.from(context); + } + + + @Override + public int getCount() { + return trends.size(); + } + + @Override + public Object getItem(int position) { + return trends.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + + + final String tag = (String) getItem(position); + final ViewHolderTag holder; + View v = convertView; + if (v == null) { + v = layoutInflater.inflate(R.layout.drawer_tag_trends, parent, false); + holder = new ViewHolderTag(); + holder.tag_name = v.findViewById(R.id.tag_name); + holder.tag_stats = v.findViewById(R.id.tag_stats); + holder.count = v.findViewById(R.id.count); + holder.chart = v.findViewById(R.id.chart); + v.setTag(holder); + } else { + holder = (ViewHolderTag) v.getTag(); + } + Trends trend = trends.get(position); + List trendsHistory = trend.getTrendsHistory(); + int people = 0; + int days = 0; + int uses = 0; + + LinkedHashMap tendency = new LinkedHashMap<>(); + for(TrendsHistory _th : trendsHistory) { + people += _th.getAccounts(); + days ++; + uses += _th.getUses(); + tendency.put(_th.getDays(), _th.getUses()); + } + people = people / days; + uses = uses / days; + holder.count.setText(uses); + holder.tag_stats.setText(context.getString(R.string.talking_about, people)); + holder.tag_name.setText(String.format("#%s", tag)); + holder.tag_name.setPaintFlags(holder.tag_name.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); + holder.tag_name.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(context, HashTagActivity.class); + Bundle b = new Bundle(); + b.putString("tag", tag.trim()); + intent.putExtras(b); + context.startActivity(intent); + } + }); + + + List trendsEntry = new ArrayList<>(); + + Iterator it = tendency.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + trendsEntry.add(new Entry((long) pair.getKey(), (int) pair.getValue())); + it.remove(); + } + LineDataSet dataSetBoosts = new LineDataSet(trendsEntry,context.getString(R.string.trending)); + dataSetBoosts.setColor(ContextCompat.getColor(context, R.color.colorAccent)); + dataSetBoosts.setValueTextSize(10f); + dataSetBoosts.setValueTextColor(ContextCompat.getColor(context, R.color.colorAccent)); + dataSetBoosts.setFillColor(ContextCompat.getColor(context, R.color.colorAccent)); + dataSetBoosts.setDrawValues(false); + dataSetBoosts.setDrawFilled(true); + dataSetBoosts.setDrawCircles(false); + dataSetBoosts.setDrawCircleHole(false); + dataSetBoosts.setLineWidth(2f); + dataSetBoosts.setMode(LineDataSet.Mode.CUBIC_BEZIER); + + List dataSets = new ArrayList<>(); + + + dataSets.add(dataSetBoosts); + //X axis + XAxis xAxis = holder.chart.getXAxis(); + xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); + xAxis.setLabelRotationAngle(45); + xAxis.setTextSize(14f); + xAxis.setDrawAxisLine(true); + xAxis.setDrawGridLines(false); + + LineData data = new LineData(dataSets); + holder.chart.setData(data); + holder.chart.invalidate(); + return v; + } + + + + private class ViewHolderTag { + TextView tag_name; + TextView tag_stats; + TextView count; + LineChart chart; + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_tag_trends.xml b/app/src/main/res/layout/drawer_tag_trends.xml new file mode 100644 index 000000000..91c99ea8f --- /dev/null +++ b/app/src/main/res/layout/drawer_tag_trends.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e6b23790..4b8964c7d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1203,4 +1203,6 @@ Remove UTM parameters The app will automatically remove UTM parameters from URLs before visiting a link. Trends + Trending now + %d people talking \ No newline at end of file