diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 110394f26..a1fbc910d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -194,6 +194,11 @@
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
/>
+
. */
+package fr.gouv.etalab.mastodon.activities;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.widget.Toolbar;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import es.dmoral.toasty.Toasty;
+import fr.gouv.etalab.mastodon.R;
+import fr.gouv.etalab.mastodon.asynctasks.RetrieveOpenCollectiveAsyncTask;
+import fr.gouv.etalab.mastodon.client.Entities.Account;
+import fr.gouv.etalab.mastodon.client.Entities.Results;
+import fr.gouv.etalab.mastodon.drawers.AccountSearchDevAdapter;
+import fr.gouv.etalab.mastodon.helper.ExpandableHeightListView;
+import fr.gouv.etalab.mastodon.helper.Helper;
+import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRemoteAccountInterface;
+
+import static fr.gouv.etalab.mastodon.helper.Helper.THEME_LIGHT;
+
+
+/**
+ * Created by Thomas on 08/02/2019.
+ * Opencollective activity
+ */
+
+public class OpencollectiveActivity extends BaseActivity implements OnRetrieveRemoteAccountInterface {
+
+ private List bakers = new ArrayList<>();
+ private List sponsors = new ArrayList<>();
+
+ private AccountSearchDevAdapter backersAdapter;
+ private AccountSearchDevAdapter sponsorsAdapter;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK);
+ switch (theme){
+ case Helper.THEME_LIGHT:
+ setTheme(R.style.AppTheme);
+ break;
+ case Helper.THEME_DARK:
+ setTheme(R.style.AppThemeDark);
+ break;
+ case Helper.THEME_BLACK:
+ setTheme(R.style.AppThemeBlack);
+ break;
+ default:
+ setTheme(R.style.AppThemeDark);
+ }
+
+ if( getSupportActionBar() != null)
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ ActionBar actionBar = getSupportActionBar();
+ if( actionBar != null ) {
+ LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ assert inflater != null;
+ @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.simple_bar, null);
+ actionBar.setCustomView(view, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+ ImageView toolbar_close = actionBar.getCustomView().findViewById(R.id.toolbar_close);
+ TextView toolbar_title = actionBar.getCustomView().findViewById(R.id.toolbar_title);
+ toolbar_close.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+ toolbar_title.setText(R.string.action_about);
+ if (theme == THEME_LIGHT){
+ Toolbar toolbar = actionBar.getCustomView().findViewById(R.id.toolbar);
+ Helper.colorizeToolbar(toolbar, R.color.black, OpencollectiveActivity.this);
+ }
+ }
+ setContentView(R.layout.activity_opencollective);
+
+
+ ExpandableHeightListView lv_backers = findViewById(R.id.lv_backers);
+ ExpandableHeightListView lv_sponsors = findViewById(R.id.lv_sponsors);
+
+
+ Button about_opencollective = findViewById(R.id.about_opencollective);
+
+ about_opencollective.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://opencollective.com/mastalab"));
+ startActivity(browserIntent);
+ }
+ });
+
+ setTitle("Open Collective");
+ lv_backers.setExpanded(true);
+ lv_sponsors.setExpanded(true);
+
+
+ backersAdapter = new AccountSearchDevAdapter(OpencollectiveActivity.this, bakers);
+ lv_backers.setAdapter(backersAdapter);
+ sponsorsAdapter = new AccountSearchDevAdapter(OpencollectiveActivity.this, sponsors);
+ lv_sponsors.setAdapter(sponsorsAdapter);
+
+ new RetrieveOpenCollectiveAsyncTask(getApplicationContext(), RetrieveOpenCollectiveAsyncTask.Type.BACKERS, OpencollectiveActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ new RetrieveOpenCollectiveAsyncTask(getApplicationContext(), RetrieveOpenCollectiveAsyncTask.Type.SPONSORS, OpencollectiveActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+
+ @Override
+ public void onRetrieveRemoteAccount(Results results) {
+ SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
+ if( results == null){
+ Toasty.error(getApplicationContext(), getString(R.string.toast_error),Toast.LENGTH_LONG).show();
+ return;
+ }
+ List accounts = results.getAccounts();
+ if( accounts != null && accounts.size() > 0){
+ if( accounts.get(0).getSocial().equals("OPENCOLLECTIVE_BACKER")){
+ bakers.addAll(accounts);
+ backersAdapter.notifyDataSetChanged();
+ }else if( accounts.get(0).getSocial().equals("OPENCOLLECTIVE_SPONSOR")){
+ sponsors.addAll(accounts);
+ sponsorsAdapter.notifyDataSetChanged();
+ }
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveOpenCollectiveAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveOpenCollectiveAsyncTask.java
new file mode 100644
index 000000000..000a60da4
--- /dev/null
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveOpenCollectiveAsyncTask.java
@@ -0,0 +1,64 @@
+/* Copyright 2019 Thomas Schneider
+ *
+ * This file is a part of Mastalab
+ *
+ * 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.
+ *
+ * Mastalab 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 Mastalab; if not,
+ * see . */
+package fr.gouv.etalab.mastodon.asynctasks;
+
+import android.content.Context;
+import android.os.AsyncTask;
+
+import java.lang.ref.WeakReference;
+
+import fr.gouv.etalab.mastodon.client.API;
+import fr.gouv.etalab.mastodon.client.Entities.Results;
+import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRemoteAccountInterface;
+
+
+/**
+ * Created by Thomas on 07/02/2019.
+ * Retrieve backers and sponsors
+ */
+
+
+public class RetrieveOpenCollectiveAsyncTask extends AsyncTask {
+
+ private OnRetrieveRemoteAccountInterface listener;
+ private String url;
+ private Results results;
+ private WeakReference contextReference;
+ private Type type;
+
+ public enum Type{
+ BACKERS,
+ SPONSORS
+ }
+
+ public RetrieveOpenCollectiveAsyncTask(Context context, Type type, OnRetrieveRemoteAccountInterface onRetrieveRemoteAccountInterface){
+ this.type = type;
+ this.listener = onRetrieveRemoteAccountInterface;
+ this.contextReference = new WeakReference<>(context);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ API api = new API(this.contextReference.get());
+ results = api.getOpencollectiveAccounts(type);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ listener.onRetrieveRemoteAccount(results);
+ }
+
+}
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java
index 11fa86fdd..d6a975d31 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java
@@ -20,6 +20,7 @@ import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
@@ -42,6 +43,7 @@ import java.util.Map;
import fr.gouv.etalab.mastodon.R;
import fr.gouv.etalab.mastodon.activities.MainActivity;
+import fr.gouv.etalab.mastodon.asynctasks.RetrieveOpenCollectiveAsyncTask;
import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoAsyncTask;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.client.Entities.Application;
@@ -1492,6 +1494,35 @@ public class API {
}
+ /**
+ * Retrieves opencollective accounts *synchronously*
+ * @return APIResponse
+ */
+ public Results getOpencollectiveAccounts(RetrieveOpenCollectiveAsyncTask.Type type){
+
+ results = new Results();
+ accounts = new ArrayList<>();
+ try {
+ HttpsConnection httpsConnection = new HttpsConnection(context);
+ String response = httpsConnection.get("https://opencollective.com/mastalab/members/all.json", 60, null, prefKeyOauthTokenT);
+ accounts = parseOpencollectiveAccountResponse(context, type, new JSONArray(response));
+ } catch (HttpsConnection.HttpsConnectionException e) {
+ setError(e.getStatusCode(), e);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (KeyManagementException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ Log.v(Helper.TAG,"accounts: " + accounts.size());
+ results.setAccounts(accounts);
+ return results;
+ }
+
+
/**
* Retrieves blocked domains for the authenticated account *synchronously*
@@ -4045,6 +4076,66 @@ public class API {
}
+ private List parseOpencollectiveAccountResponse(Context context, RetrieveOpenCollectiveAsyncTask.Type type, JSONArray jsonArray){
+ List accounts = new ArrayList<>();
+ try {
+ int i = 0;
+ while (i < jsonArray.length() ) {
+ JSONObject resobj = jsonArray.getJSONObject(i);
+ Account account = parseOpencollectiveAccountResponse(context, type, resobj);
+ if( type == RetrieveOpenCollectiveAsyncTask.Type.BACKERS && account.getSocial() != null && account.getSocial().equals("OPENCOLLECTIVE_BACKER"))
+ accounts.add(account);
+ else if( type == RetrieveOpenCollectiveAsyncTask.Type.SPONSORS && account.getSocial() != null && account.getSocial().equals("OPENCOLLECTIVE_SPONSOR"))
+ accounts.add(account);
+ i++;
+ }
+ } catch (JSONException e) {
+ setDefaultError(e);
+ }
+ return accounts;
+ }
+
+ /**
+ * Parse json response an unique account
+ * @param resobj JSONObject
+ * @return Account
+ */
+ @SuppressWarnings("InfiniteRecursion")
+ private static Account parseOpencollectiveAccountResponse(Context context, RetrieveOpenCollectiveAsyncTask.Type type, JSONObject resobj){
+
+ Account account = new Account();
+ try {
+ account.setId(resobj.get("MemberId").toString());
+ account.setUuid(resobj.get("MemberId").toString());
+ account.setUsername(resobj.get("name").toString());
+ account.setAcct(resobj.get("tier").toString());
+ account.setDisplay_name(resobj.get("name").toString());
+ account.setLocked(false);
+ account.setCreated_at(Helper.opencollectivetStringToDate(context, resobj.get("createdAt").toString()));
+ account.setFollowers_count(0);
+ account.setFollowing_count(0);
+ account.setStatuses_count(0);
+ account.setNote(resobj.get("description").toString());
+ account.setBot(false);
+ account.setMoved_to_account(null);
+ account.setUrl(resobj.get("profile").toString());
+ account.setAvatar(resobj.get("image").toString());
+ account.setAvatar_static(resobj.get("image").toString());
+ account.setHeader(null);
+ account.setHeader_static(null);
+ if(resobj.get("role").toString().equals("BACKER"))
+ account.setSocial("OPENCOLLECTIVE_BACKER");
+ else if(resobj.get("role").toString().equals("SPONSOR"))
+ account.setSocial("OPENCOLLECTIVE_SPONSOR");
+ else
+ account.setSocial("OPENCOLLECTIVE");
+
+ } catch (JSONException ignored) {} catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return account;
+ }
+
/**
* Parse json response an unique account
* @param resobj JSONObject
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/GNUAPI.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/GNUAPI.java
index 349b910ea..dc87f4a90 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/client/GNUAPI.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/GNUAPI.java
@@ -235,15 +235,13 @@ public class GNUAPI {
*/
public Relationship getRelationship(String accountId) {
- List relationships;
+
Relationship relationship = null;
HashMap params = new HashMap<>();
- params.put("user_id",accountId);
+ params.put("target_id",accountId);
try {
String response = new HttpsConnection(context).get(getAbsoluteUrl("/friendships/show.json"), 60, params, prefKeyOauthTokenT);
- relationships = parseRelationshipResponse(new JSONArray(response));
- if( relationships != null && relationships.size() > 0)
- relationship = relationships.get(0);
+ relationship = parseRelationshipResponse(new JSONObject(response));
} catch (HttpsConnection.HttpsConnectionException e) {
setError(e.getStatusCode(), e);
} catch (NoSuchAlgorithmException e) {
@@ -271,9 +269,9 @@ public class GNUAPI {
if( accounts != null && accounts.size() > 0 ) {
StringBuilder parameters = new StringBuilder();
for(Account account: accounts)
- parameters.append("user_id[]=").append(account.getId()).append("&");
- parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(10));
- params.put("user_id[]", parameters.toString());
+ parameters.append("target_id[]=").append(account.getId()).append("&");
+ parameters = new StringBuilder(parameters.substring(0, parameters.length() - 1).substring(12));
+ params.put("target_id[]", parameters.toString());
List relationships = new ArrayList<>();
try {
HttpsConnection httpsConnection = new HttpsConnection(context);
@@ -1895,22 +1893,28 @@ public class GNUAPI {
/**
* Parse json response an unique relationship
- * @param resobj JSONObject
+ * @param resobjIni JSONObject
* @return Relationship
*/
- private Relationship parseRelationshipResponse(JSONObject resobj){
+ private Relationship parseRelationshipResponse(JSONObject resobjIni){
+
Relationship relationship = new Relationship();
try {
+ JSONObject resobj = resobjIni.getJSONObject("relationship").getJSONObject("source");
relationship.setId(resobj.get("id").toString());
relationship.setFollowing(Boolean.valueOf(resobj.get("following").toString()));
relationship.setFollowed_by(Boolean.valueOf(resobj.get("followed_by").toString()));
relationship.setBlocking(Boolean.valueOf(resobj.get("blocking").toString()));
- relationship.setMuting(Boolean.valueOf(resobj.get("muting").toString()));
+ try {
+ relationship.setMuting(Boolean.valueOf(resobj.get("muting").toString()));
+ }catch (Exception ignored){
+ relationship.setMuting(false);
+ }
try {
relationship.setMuting_notifications(!Boolean.valueOf(resobj.get("notifications_enabled").toString()));
}catch (Exception ignored){
- relationship.setMuting_notifications(true);
+ relationship.setMuting_notifications(false);
}
relationship.setEndorsed(false);
relationship.setShowing_reblogs(true);
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchDevAdapter.java b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchDevAdapter.java
index 325d3b163..fd6c40e41 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchDevAdapter.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchDevAdapter.java
@@ -110,45 +110,53 @@ public class AccountSearchDevAdapter extends BaseAdapter implements OnPostAction
}else{
holder.account_dn.setCompoundDrawables( null, null, null, null);
}
-
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- assert account != null;
- holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true));
- holder.account_un.setText(String.format("@%s",account.getAcct()));
- }else {
- assert account != null;
- holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true));
- holder.account_un.setText(String.format("@%s",account.getAcct()));
- }
- Helper.changeDrawableColor(context, R.drawable.ic_lock_outline,R.color.mastodonC4);
- //Profile picture
- Glide.with(holder.account_pp.getContext())
- .load(account.getAvatar())
- .into(holder.account_pp);
if( account.isFollowing()){
holder.account_follow.hide();
}else{
holder.account_follow.show();
}
- holder.account_follow.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- holder.account_follow.setEnabled(false);
- new PostActionAsyncTask(context, API.StatusAction.FOLLOW, account.getId(), AccountSearchDevAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ if( !account.getSocial().contains("OPENCOLLECTIVE")) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ assert account != null;
+ holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true));
+ holder.account_un.setText(String.format("@%s", account.getAcct()));
+ } else {
+ assert account != null;
+ holder.account_dn.setText(Helper.shortnameToUnicode(account.getDisplay_name(), true));
+ holder.account_un.setText(String.format("@%s", account.getAcct()));
}
- });
- holder.acccount_container.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(context, ShowAccountActivity.class);
- Bundle b = new Bundle();
- b.putParcelable("account", account);
- intent.putExtras(b);
- context.startActivity(intent);
- }
- });
+ }else{
+ holder.account_un.setText(account.getAcct());
+ holder.account_follow.hide();
+ }
+ Helper.changeDrawableColor(context, R.drawable.ic_lock_outline,R.color.mastodonC4);
+ //Profile picture
+ Glide.with(holder.account_pp.getContext())
+ .load(account.getAvatar())
+ .into(holder.account_pp);
+
+
+ if( !account.getSocial().contains("OPENCOLLECTIVE")) {
+
+ holder.account_follow.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ holder.account_follow.setEnabled(false);
+ new PostActionAsyncTask(context, API.StatusAction.FOLLOW, account.getId(), AccountSearchDevAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+ });
+ holder.acccount_container.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(context, ShowAccountActivity.class);
+ Bundle b = new Bundle();
+ b.putParcelable("account", account);
+ intent.putExtras(b);
+ context.startActivity(intent);
+ }
+ });
+ }
return convertView;
}
diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java
index c338bbce0..3a067be3d 100644
--- a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java
+++ b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java
@@ -618,6 +618,29 @@ public class Helper {
}
}
+ /**
+ * Convert String date from Mastodon
+ * @param context Context
+ * @param date String
+ * @return Date
+ */
+ public static Date opencollectivetStringToDate(Context context, String date) throws ParseException {
+ Locale userLocale;
+ if (date == null )
+ return null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ userLocale = context.getResources().getConfiguration().getLocales().get(0);
+ } else {
+ //noinspection deprecation
+ userLocale = context.getResources().getConfiguration().locale;
+ }
+ String STRING_DATE_FORMAT;
+ STRING_DATE_FORMAT = "yyyy-MM-dd HH:mm";
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(STRING_DATE_FORMAT, userLocale);
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("gmt"));
+ simpleDateFormat.setLenient(true);
+ return simpleDateFormat.parse(date);
+ }
/**
* Convert a date in String -> format yyyy-MM-dd HH:mm:ss
diff --git a/app/src/main/res/drawable-anydpi/ic_attach_money.xml b/app/src/main/res/drawable-anydpi/ic_attach_money.xml
new file mode 100644
index 000000000..b520fc98d
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_attach_money.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_opencollective.xml b/app/src/main/res/layout/activity_opencollective.xml
new file mode 100644
index 000000000..53685c0e3
--- /dev/null
+++ b/app/src/main/res/layout/activity_opencollective.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
index dcec71c92..a1cd9c140 100644
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -159,6 +159,10 @@
android:id="@+id/nav_main_info">
Optimize loading time
All
+ Support the app