From 064a6545d134bf6aea4069b3101a3f1a55a9eb28 Mon Sep 17 00:00:00 2001 From: stom79 Date: Sun, 3 Feb 2019 09:27:02 +0100 Subject: [PATCH] Home timeline + fixes --- .../mastodon/activities/BaseMainActivity.java | 75 ++++--- .../mastodon/activities/LoginActivity.java | 199 +++++++++--------- .../asynctasks/RetrieveFeedsAsyncTask.java | 41 +++- .../gouv/etalab/mastodon/client/GNUAPI.java | 102 ++------- .../fragments/DisplayStatusFragment.java | 2 +- .../gouv/etalab/mastodon/helper/Helper.java | 19 +- .../main/res/drawable/gnu_default_avatar.png | Bin 0 -> 25509 bytes 7 files changed, 222 insertions(+), 216 deletions(-) create mode 100644 app/src/main/res/drawable/gnu_default_avatar.png diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java index 178972115..617944b1a 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/BaseMainActivity.java @@ -254,7 +254,8 @@ public abstract class BaseMainActivity extends BaseActivity social = UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED; else if( account.getSocial().equals("PLEROMA")) social = UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA; - + else if( account.getSocial().equals("GNU")) + social = UpdateAccountInfoAsyncTask.SOCIAL.GNU; countNewStatus = 0; countNewNotifications = 0; @@ -343,7 +344,7 @@ public abstract class BaseMainActivity extends BaseActivity delete_all = findViewById(R.id.delete_all); add_new = findViewById(R.id.add_new); - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { TabLayout.Tab tabHome = tabLayout.newTab(); TabLayout.Tab tabNotif = tabLayout.newTab(); TabLayout.Tab tabDirect = tabLayout.newTab(); @@ -419,47 +420,66 @@ public abstract class BaseMainActivity extends BaseActivity tabLayout.addTab(tabHome); tabLayout.addTab(tabNotif); tabPosition.put("home",0); - typePosition.put(0, RetrieveFeedsAsyncTask.Type.HOME); + if( social != UpdateAccountInfoAsyncTask.SOCIAL.GNU ) + typePosition.put(0, RetrieveFeedsAsyncTask.Type.HOME); + else + typePosition.put(0, RetrieveFeedsAsyncTask.Type.GNU_HOME); tabPosition.put("notifications",1); - typePosition.put(1, RetrieveFeedsAsyncTask.Type.NOTIFICATION); + if( social != UpdateAccountInfoAsyncTask.SOCIAL.GNU ) + typePosition.put(1, RetrieveFeedsAsyncTask.Type.NOTIFICATION); + else + typePosition.put(0, RetrieveFeedsAsyncTask.Type.GNU_NOTIFICATION); int i = 2; if( display_direct) { tabLayout.addTab(tabDirect); tabPosition.put("direct",i); - userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(getApplicationContext())); + if( social != UpdateAccountInfoAsyncTask.SOCIAL.GNU ) { + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(getApplicationContext())); - String instanceVersion = sharedpreferences.getString(Helper.INSTANCE_VERSION + userId + instance, null); - if (instanceVersion != null) { - Version currentVersion = new Version(instanceVersion); - Version minVersion = new Version("2.6"); - if (currentVersion.compareTo(minVersion) == 1 || currentVersion.equals(minVersion)) { - typePosition.put(i, RetrieveFeedsAsyncTask.Type.CONVERSATION); + String instanceVersion = sharedpreferences.getString(Helper.INSTANCE_VERSION + userId + instance, null); + if (instanceVersion != null) { + Version currentVersion = new Version(instanceVersion); + Version minVersion = new Version("2.6"); + if (currentVersion.compareTo(minVersion) == 1 || currentVersion.equals(minVersion)) { + typePosition.put(i, RetrieveFeedsAsyncTask.Type.CONVERSATION); + } else { + typePosition.put(i, RetrieveFeedsAsyncTask.Type.DIRECT); + } } else { typePosition.put(i, RetrieveFeedsAsyncTask.Type.DIRECT); } }else{ - typePosition.put(i, RetrieveFeedsAsyncTask.Type.DIRECT); + typePosition.put(i, RetrieveFeedsAsyncTask.Type.GNU_DM); } i++; } if( display_local) { tabLayout.addTab(tabLocal); tabPosition.put("local", i); - typePosition.put(i, RetrieveFeedsAsyncTask.Type.LOCAL); + if( social != UpdateAccountInfoAsyncTask.SOCIAL.GNU ) + typePosition.put(i, RetrieveFeedsAsyncTask.Type.LOCAL); + else + typePosition.put(i, RetrieveFeedsAsyncTask.Type.GNU_LOCAL); i++; } if( display_global) { tabLayout.addTab(tabPublic); tabPosition.put("global", i); - typePosition.put(i, RetrieveFeedsAsyncTask.Type.PUBLIC); + if( social != UpdateAccountInfoAsyncTask.SOCIAL.GNU ) + typePosition.put(i, RetrieveFeedsAsyncTask.Type.PUBLIC); + else + typePosition.put(i, RetrieveFeedsAsyncTask.Type.GNU_WHOLE); i++; } if( display_art) { tabLayout.addTab(tabArt); tabPosition.put("art", i); - typePosition.put(i, RetrieveFeedsAsyncTask.Type.ART); + if( social != UpdateAccountInfoAsyncTask.SOCIAL.GNU ) + typePosition.put(i, RetrieveFeedsAsyncTask.Type.ART); + else + typePosition.put(i, RetrieveFeedsAsyncTask.Type.GNU_ART); } if( (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) @@ -1084,10 +1104,10 @@ public abstract class BaseMainActivity extends BaseActivity startSreaming(); - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) Helper.refreshSearchTag(BaseMainActivity.this, tabLayout, adapter); int tabCount = tabLayout.getTabCount(); - if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) + if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) for( int j = countPage ; j < tabCount ; j++){ attacheDelete(j); } @@ -1207,7 +1227,7 @@ public abstract class BaseMainActivity extends BaseActivity tabLayout.getTabAt(0).select(); - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { toot.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -1491,7 +1511,7 @@ public abstract class BaseMainActivity extends BaseActivity // Asked once for notification opt-in boolean popupShown = sharedpreferences.getBoolean(Helper.SET_POPUP_PUSH, false); - if(!popupShown && (social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA)){ + if(!popupShown && (social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU)){ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(BaseMainActivity.this, style); LayoutInflater inflater = getLayoutInflater(); @SuppressLint("InflateParams") View dialogView = inflater.inflate(R.layout.popup_quick_settings, null); @@ -1531,7 +1551,7 @@ public abstract class BaseMainActivity extends BaseActivity mamageNewIntent(getIntent()); - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { // Retrieves instance new RetrieveInstanceAsyncTask(getApplicationContext(), BaseMainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -2430,14 +2450,14 @@ public abstract class BaseMainActivity extends BaseActivity } public void removeTabPage() { - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { this.mNumOfTabs--; notifyDataSetChanged(); } } public void addTabPage(String title) { - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { TabLayout.Tab tab = tabLayout.newTab(); tab.setText(title); this.mNumOfTabs++; @@ -2447,7 +2467,7 @@ public abstract class BaseMainActivity extends BaseActivity @Override public Fragment getItem(int position) { - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { //Remove the search bar if (!toolbar_search.isIconified()) { toolbarTitle.setVisibility(View.VISIBLE); @@ -2459,7 +2479,10 @@ public abstract class BaseMainActivity extends BaseActivity Bundle bundle = new Bundle(); if (position == 0) { homeFragment = new DisplayStatusFragment(); - bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.HOME); + if(social != UpdateAccountInfoAsyncTask.SOCIAL.GNU) + bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.HOME); + else + bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.GNU_HOME); homeFragment.setArguments(bundle); return homeFragment; } else if (position == 1) { @@ -2560,7 +2583,7 @@ public abstract class BaseMainActivity extends BaseActivity public Object instantiateItem(@NonNull ViewGroup container, int position) { Fragment createdFragment = (Fragment) super.instantiateItem(container, position); registeredFragments.put(position, createdFragment); - if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA || social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { // save the appropriate reference depending on position if (position == 0) { diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java index b352fe90c..8bca03e98 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java @@ -238,7 +238,6 @@ public class LoginActivity extends BaseActivity { case R.id.set_gnu: login_uid.setHint(R.string.username); connectionButton.setEnabled(true); - connectionButton.setClickable(true); socialNetwork = UpdateAccountInfoAsyncTask.SOCIAL.GNU; break; } @@ -446,112 +445,111 @@ public class LoginActivity extends BaseActivity { super.onResume(); if (login_instance != null &&login_instance.getText() != null && login_instance.getText().toString().length() > 0 && client_id_for_webview) { - if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.GNU) { - connectionButton.setEnabled(false); - client_id_for_webview = false; - retrievesClientId(); - } + connectionButton.setEnabled(false); + client_id_for_webview = false; + retrievesClientId(); } } private void retrievesClientId(){ - if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.GNU) - return; - String instanceFromField = login_instance.getText().toString().trim(); - final String[] host = new String[1]; - try { - URL url = new URL(instanceFromField); - host[0] = url.getHost(); - } catch (MalformedURLException e) { - host[0] = instanceFromField; - } - try { - instance = URLEncoder.encode(host[0], "utf-8"); - } catch (UnsupportedEncodingException e) { - Toasty.error(LoginActivity.this,getString(R.string.client_error), Toast.LENGTH_LONG).show(); - } - if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) - actionToken = "/api/v1/apps"; - else - actionToken = "/api/v1/oauth-clients/local"; - final HashMap parameters = new HashMap<>(); - parameters.put(Helper.CLIENT_NAME, Helper.CLIENT_NAME_VALUE); - parameters.put(Helper.REDIRECT_URIS, client_id_for_webview?Helper.REDIRECT_CONTENT_WEB:Helper.REDIRECT_CONTENT); - if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) { - parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES); - }else { - parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES_PEERTUBE); - } + if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.GNU){ + String instanceFromField = login_instance.getText().toString().trim(); + String host = instanceFromField; - parameters.put(Helper.WEBSITE, Helper.WEBSITE_VALUE); - new Thread(new Runnable(){ - @Override - public void run() { - try { - String response; - if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) - response = new HttpsConnection(LoginActivity.this).post(Helper.instanceWithProtocol(instance) + actionToken, 30, parameters, null ); - else - response = new HttpsConnection(LoginActivity.this).get(Helper.instanceWithProtocol(instance) + actionToken, 30, parameters, null ); - - runOnUiThread(new Runnable() { - public void run() { - JSONObject resobj; - try { - resobj = new JSONObject(response); - client_id = resobj.get(Helper.CLIENT_ID).toString(); - client_secret = resobj.get(Helper.CLIENT_SECRET).toString(); - String id = null; - if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) - id = resobj.get(Helper.ID).toString(); - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.CLIENT_ID, client_id); - editor.putString(Helper.CLIENT_SECRET, client_secret); - editor.putString(Helper.ID, id); - editor.apply(); - connectionButton.setEnabled(true); - if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) { - login_two_step.setVisibility(View.VISIBLE); - info_2FA.setVisibility(View.VISIBLE); - showCase2FA(true); - } - if( client_id_for_webview){ - boolean embedded_browser = sharedpreferences.getBoolean(Helper.SET_EMBEDDED_BROWSER, true); - if( embedded_browser) { - Intent i = new Intent(LoginActivity.this, WebviewConnectActivity.class); - i.putExtra("social", socialNetwork); - i.putExtra("instance", instance); - startActivity(i); - }else{ - String url = redirectUserToAuthorizeAndLogin(client_id, instance); - Helper.openBrowser(LoginActivity.this, url); - } - } - } catch (JSONException ignored) {ignored.printStackTrace();} - } - }); - } catch (final Exception e) { - e.printStackTrace(); - runOnUiThread(new Runnable() { - public void run() { - String message; - if( e.getLocalizedMessage() != null && e.getLocalizedMessage().trim().length() > 0) - message = e.getLocalizedMessage(); - else if (e.getMessage() != null && e.getMessage().trim().length() > 0) - message = e.getMessage(); - else - message = getString(R.string.client_error); - Toasty.error(getApplicationContext(), message,Toast.LENGTH_LONG).show(); - } - }); - } + try { + URL url = new URL(instanceFromField); + host = url.getHost(); + } catch (MalformedURLException e) { } + try { + instance = URLEncoder.encode(host, "utf-8"); + } catch (UnsupportedEncodingException e) { + Toasty.error(LoginActivity.this,getString(R.string.client_error), Toast.LENGTH_LONG).show(); + } + if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) + actionToken = "/api/v1/apps"; + else + actionToken = "/api/v1/oauth-clients/local"; + final HashMap parameters = new HashMap<>(); + parameters.put(Helper.CLIENT_NAME, Helper.CLIENT_NAME_VALUE); + parameters.put(Helper.REDIRECT_URIS, client_id_for_webview?Helper.REDIRECT_CONTENT_WEB:Helper.REDIRECT_CONTENT); + if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) { + parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES); + }else { + parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES_PEERTUBE); } - }).start(); + parameters.put(Helper.WEBSITE, Helper.WEBSITE_VALUE); + + new Thread(new Runnable(){ + @Override + public void run() { + try { + String response; + if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) + response = new HttpsConnection(LoginActivity.this).post(Helper.instanceWithProtocol(instance) + actionToken, 30, parameters, null ); + else + response = new HttpsConnection(LoginActivity.this).get(Helper.instanceWithProtocol(instance) + actionToken, 30, parameters, null ); + + runOnUiThread(new Runnable() { + public void run() { + JSONObject resobj; + try { + resobj = new JSONObject(response); + client_id = resobj.get(Helper.CLIENT_ID).toString(); + client_secret = resobj.get(Helper.CLIENT_SECRET).toString(); + String id = null; + if( socialNetwork != UpdateAccountInfoAsyncTask.SOCIAL.PEERTUBE) + id = resobj.get(Helper.ID).toString(); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.CLIENT_ID, client_id); + editor.putString(Helper.CLIENT_SECRET, client_secret); + editor.putString(Helper.ID, id); + editor.apply(); + connectionButton.setEnabled(true); + if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) { + login_two_step.setVisibility(View.VISIBLE); + info_2FA.setVisibility(View.VISIBLE); + showCase2FA(true); + } + if( client_id_for_webview){ + boolean embedded_browser = sharedpreferences.getBoolean(Helper.SET_EMBEDDED_BROWSER, true); + if( embedded_browser) { + Intent i = new Intent(LoginActivity.this, WebviewConnectActivity.class); + i.putExtra("social", socialNetwork); + i.putExtra("instance", instance); + startActivity(i); + }else{ + String url = redirectUserToAuthorizeAndLogin(client_id, instance); + Helper.openBrowser(LoginActivity.this, url); + } + } + } catch (JSONException ignored) {ignored.printStackTrace();} + } + }); + } catch (final Exception e) { + e.printStackTrace(); + runOnUiThread(new Runnable() { + public void run() { + String message; + if( e.getLocalizedMessage() != null && e.getLocalizedMessage().trim().length() > 0) + message = e.getLocalizedMessage(); + else if (e.getMessage() != null && e.getMessage().trim().length() > 0) + message = e.getMessage(); + else + message = getString(R.string.client_error); + Toasty.error(getApplicationContext(), message,Toast.LENGTH_LONG).show(); + } + }); + } + } + }).start(); + }else{ + connectionButton.setEnabled(true); + } connectionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -588,14 +586,15 @@ public class LoginActivity extends BaseActivity { oauthUrl = "/api/v1/users/token"; }else if( socialNetwork == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { String instanceFromField = login_instance.getText().toString().trim(); + String host; try { URL url = new URL(instanceFromField); - host[0] = url.getHost(); + host = url.getHost(); } catch (MalformedURLException e) { - host[0] = instanceFromField; + host = instanceFromField; } try { - instance = URLEncoder.encode(host[0], "utf-8"); + instance = URLEncoder.encode(host, "utf-8"); } catch (UnsupportedEncodingException e) { } String username = login_uid.getText().toString().trim().toLowerCase(); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java index 5df64a0b8..938d323ca 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java @@ -27,6 +27,7 @@ import fr.gouv.etalab.mastodon.client.APIResponse; import fr.gouv.etalab.mastodon.client.Entities.Peertube; import fr.gouv.etalab.mastodon.client.Entities.RemoteInstance; import fr.gouv.etalab.mastodon.client.Entities.TagTimeline; +import fr.gouv.etalab.mastodon.client.GNUAPI; import fr.gouv.etalab.mastodon.client.PeertubeAPI; import fr.gouv.etalab.mastodon.helper.FilterToots; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveFeedsInterface; @@ -72,13 +73,10 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { ONESTATUS, CONTEXT, TAG, - CACHE_BOOKMARKS, - CACHE_BOOKMARKS_PEERTUBE, - CACHE_STATUS, REMOTE_INSTANCE, ART, NOTIFICATION, - PIXELFED, + PSUBSCRIPTIONS, POVERVIEW, PTRENDING, @@ -87,11 +85,27 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { PLOCAL, CHANNEL, MYVIDEOS, + + PIXELFED, PF_HOME, PF_LOCAL, PF_DISCOVER, PF_NOTIFICATION, - SCHEDULED_TOOTS + + + GNU_HOME, + GNU_LOCAL, + GNU_WHOLE, + GNU_NOTIFICATION, + GNU_DM, + GNU_ART, + GNU_TAG, + + SCHEDULED_TOOTS, + CACHE_BOOKMARKS, + CACHE_BOOKMARKS_PEERTUBE, + CACHE_STATUS, + } @@ -314,6 +328,23 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { break; case HASHTAG: break; + + case GNU_HOME: + GNUAPI gnuAPI = new GNUAPI(this.contextReference.get()); + apiResponse = gnuAPI.getHomeTimeline(max_id); + break; + case GNU_LOCAL: + gnuAPI = new GNUAPI(this.contextReference.get()); + break; + case GNU_WHOLE: + gnuAPI = new GNUAPI(this.contextReference.get()); + break; + case GNU_NOTIFICATION: + gnuAPI = new GNUAPI(this.contextReference.get()); + break; + case GNU_DM: + gnuAPI = new GNUAPI(this.contextReference.get()); + break; } if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { List bookmarks = new StatusCacheDAO(contextReference.get(), db).getAllStatusId(StatusCacheDAO.BOOKMARK_CACHE); 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 c39ff0eea..3edd0efb5 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 @@ -53,6 +53,8 @@ import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.sqlite.AccountDAO; import fr.gouv.etalab.mastodon.sqlite.Sqlite; +import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_OAUTH_TOKEN; + /** * Created by Thomas on 02/02/2019. @@ -88,7 +90,7 @@ public class GNUAPI { tootPerPage = sharedpreferences.getInt(Helper.SET_TOOTS_PER_PAGE, 40); accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40); notificationPerPage = sharedpreferences.getInt(Helper.SET_NOTIFICATIONS_PER_PAGE, 15); - this.prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + this.prefKeyOauthTokenT = sharedpreferences.getString(PREF_KEY_OAUTH_TOKEN, null); if( Helper.getLiveInstance(context) != null) this.instance = Helper.getLiveInstance(context); else { @@ -124,7 +126,7 @@ public class GNUAPI { if( token != null) this.prefKeyOauthTokenT = token; else - this.prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); + this.prefKeyOauthTokenT = sharedpreferences.getString(PREF_KEY_OAUTH_TOKEN, null); apiResponse = new APIResponse(); APIError = null; } @@ -689,16 +691,18 @@ public class GNUAPI { params.put("min_id", min_id); if (0 > limit || limit > 80) limit = 80; - params.put("limit",String.valueOf(limit)); + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + //Current user statuses = new ArrayList<>(); try { HttpsConnection httpsConnection = new HttpsConnection(context); - String response = httpsConnection.get(getAbsoluteUrl("/timelines/home"), 60, params, prefKeyOauthTokenT); + String response = httpsConnection.get(getAbsoluteUrl("/statuses/home_timeline.json"), 60, params, prefKeyOauthTokenT); apiResponse.setSince_id(httpsConnection.getSince_id()); apiResponse.setMax_id(httpsConnection.getMax_id()); statuses = parseStatuses(context, new JSONArray(response)); } catch (HttpsConnection.HttpsConnectionException e) { setError(e.getStatusCode(), e); + e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { @@ -780,10 +784,7 @@ public class GNUAPI { try { HttpsConnection httpsConnection = new HttpsConnection(context); String url; - if( instanceName == null) - url = getAbsoluteUrl("/timelines/public"); - else - url = getAbsoluteUrlRemoteInstance(instanceName); + url = getAbsoluteUrl("/timelines/public"); String response = httpsConnection.get(url, 60, params, prefKeyOauthTokenT); apiResponse.setSince_id(httpsConnection.getSince_id()); apiResponse.setMax_id(httpsConnection.getMax_id()); @@ -804,60 +805,6 @@ public class GNUAPI { } - /** - * Retrieves discover timeline for the account *synchronously* - * @param local boolean only local timeline - * @param max_id String id max - * @return APIResponse - */ - public APIResponse getDiscoverTimeline(boolean local, String max_id){ - return getDiscoverTimeline(local, max_id, null, tootPerPage); - } - - - /** - * Retrieves discover timeline for the account *synchronously* - * @param local boolean only local timeline - * @param max_id String id max - * @param since_id String since the id - * @param limit int limit - max value 40 - * @return APIResponse - */ - private APIResponse getDiscoverTimeline(boolean local, String max_id, String since_id, int limit){ - - HashMap params = new HashMap<>(); - if( local) - params.put("local", Boolean.toString(true)); - if( max_id != null ) - params.put("max_id", max_id); - if( since_id != null ) - params.put("since_id", since_id); - if( 0 > limit || limit > 40) - limit = 40; - params.put("limit",String.valueOf(limit)); - statuses = new ArrayList<>(); - try { - HttpsConnection httpsConnection = new HttpsConnection(context); - String url; - url = getAbsoluteUr2l("/discover/posts"); - String response = httpsConnection.get(url, 60, params, prefKeyOauthTokenT); - apiResponse.setSince_id(httpsConnection.getSince_id()); - apiResponse.setMax_id(httpsConnection.getMax_id()); - statuses = parseStatuses(context, 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(); - } - apiResponse.setStatuses(statuses); - return apiResponse; - } @@ -1815,15 +1762,18 @@ public class GNUAPI { status.setSensitive(false); status.setSpoiler_text(null); status.setVisibility("public"); - status.setLanguage(resobj.get("geo").toString()); + status.setLanguage(resobj.isNull("geo")?null:resobj.getString("geo")); status.setUrl(resobj.get("external_url").toString()); //Retrieves attachments - JSONArray arrayAttachement = resobj.getJSONArray("attachments"); - ArrayList attachments = new ArrayList<>(parseAttachmentResponse(arrayAttachement)); + try { + JSONArray arrayAttachement = resobj.getJSONArray("attachments"); + ArrayList attachments = new ArrayList<>(parseAttachmentResponse(arrayAttachement)); + status.setMedia_attachments(attachments); + }catch (Exception ignored){ status.setMedia_attachments(new ArrayList<>());} status.setCard(null); - status.setMedia_attachments(attachments); + //Retrieves mentions List mentions = new ArrayList<>(); JSONArray arrayMention = resobj.getJSONArray("attentions"); @@ -1848,13 +1798,13 @@ public class GNUAPI { try { if(resobj.getJSONObject("source") != null){ application.setName(resobj.getJSONObject("source").toString()); - application.setWebsite(null); + application.setWebsite(resobj.getJSONObject("source_link").toString()); } }catch (Exception e){ application = new Application(); } status.setApplication(application); - status.setAccount(parseAccountResponse(context, resobj.getJSONObject("account"))); + status.setAccount(parseAccountResponse(context, resobj.getJSONObject("user"))); status.setContent(resobj.get("statusnet_html").toString()); status.setFavourites_count(Integer.valueOf(resobj.get("fave_num").toString())); status.setReblogs_count(Integer.valueOf(resobj.get("repeat_num").toString())); @@ -1873,8 +1823,8 @@ public class GNUAPI { status.setPinned(false); try{ status.setReblog(parseStatuses(context, resobj.getJSONObject("retweeted_status"))); - }catch (Exception ignored){} - } catch (JSONException ignored) {} catch (ParseException e) { + }catch (Exception ignored){ status.setReblog(null);} + } catch (JSONException ignored) {ignored.printStackTrace();} catch (ParseException e) { e.printStackTrace(); } return status; @@ -2176,17 +2126,7 @@ public class GNUAPI { private String getAbsoluteUrl(String action) { - return Helper.instanceWithProtocol(this.instance) + "/api/v1" + action; - } - private String getAbsoluteUr2l(String action) { - return Helper.instanceWithProtocol(this.instance) + "/api/v2" + action; - } - private String getAbsoluteUrlRemote(String remote, String action) { - return "https://" + remote + "/api/v1" + action; - } - - private String getAbsoluteUrlRemoteInstance(String instanceName) { - return "https://" + instanceName + "/api/v1/timelines/public?local=true"; + return Helper.instanceWithProtocol(this.instance) + "/api" + action; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java index 4bbf2ed50..d5a34a9b0 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java @@ -235,7 +235,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn //Manage broadcast receiver for Mastodon timelines - if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { + if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA|| MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.GNU) { if( receive_action != null) LocalBroadcastManager.getInstance(context).unregisterReceiver(receive_action); receive_action = new BroadcastReceiver() { 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 dc181192d..6472bab45 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 @@ -599,14 +599,18 @@ public class Helper { //noinspection deprecation userLocale = context.getResources().getConfiguration().locale; } - final String STRING_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + String STRING_DATE_FORMAT; + if( !date.contains("+")) + STRING_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + else //GNU date format + STRING_DATE_FORMAT = "EEE MMM dd HH:mm:ss ZZZZZ yyyy"; SimpleDateFormat simpleDateFormat = new SimpleDateFormat(STRING_DATE_FORMAT, userLocale); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("gmt")); simpleDateFormat.setLenient(true); try { return simpleDateFormat.parse(date); }catch (Exception e){ - String newdate = date.split("\\+")[0]; + String newdate = date.split("\\+")[0].trim(); simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", userLocale); simpleDateFormat.setTimeZone(TimeZone.getTimeZone("gmt")); simpleDateFormat.setLenient(true); @@ -2801,7 +2805,7 @@ public class Helper { if( url != null && url.startsWith("/")){ url = Helper.getLiveInstanceWithProtocol(context) + url; } - if( url == null || url.equals("null") || url.contains("missing.png") || url.contains(".svg")) { + if( url == null || url.equals("null")|| url.equals("false") || url.contains("missing.png") || url.contains(".svg")) { if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON || BaseMainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PLEROMA) { try { Glide.with(imageView.getContext()) @@ -2820,6 +2824,15 @@ public class Helper { } catch (Exception ignored) { } return; + }else if( MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.GNU){ + try { + Glide.with(imageView.getContext()) + .load(R.drawable.gnu_default_avatar) + .apply(new RequestOptions().transforms(new CenterCrop(), new RoundedCorners(10))) + .into(imageView); + } catch (Exception ignored) { + } + return; } } if( !disableGif) diff --git a/app/src/main/res/drawable/gnu_default_avatar.png b/app/src/main/res/drawable/gnu_default_avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..4545718c51188e9f5ce60bb50eda95f1518d4298 GIT binary patch literal 25509 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�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