diff --git a/app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java b/app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java index d680548e4..816c03ddd 100644 --- a/app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java @@ -188,7 +188,6 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra } else if (radioGroup.getCheckedRadioButtonId() == R.id.gnu_instance) { new HttpsConnection(ReorderTimelinesActivity.this, null).get("https://" + instanceName + "/api/statuses/public_timeline.json", 10, null, null); } - runOnUiThread(new Runnable() { public void run() { dialog.dismiss(); @@ -264,78 +263,84 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra @Override public void afterTextChanged(Editable s) { - Pattern host = Pattern.compile("([\\da-z\\.-]+\\.[a-z\\.]{2,12})"); - Matcher matcher = host.matcher(s.toString().trim()); - if (s.toString().trim().length() == 0 || !matcher.find()) { - alertDialog.getButton( - AlertDialog.BUTTON_POSITIVE).setEnabled(false); - } else { - // Something into edit text. Enable the button. + if (radioGroup.getCheckedRadioButtonId() != R.id.twitter_accounts){ + Pattern host = Pattern.compile("([\\da-z\\.-]+\\.[a-z\\.]{2,12})"); + Matcher matcher = host.matcher(s.toString().trim()); + if (s.toString().trim().length() == 0 || !matcher.find()) { + alertDialog.getButton( + AlertDialog.BUTTON_POSITIVE).setEnabled(false); + } else { + // Something into edit text. Enable the button. + alertDialog.getButton( + AlertDialog.BUTTON_POSITIVE).setEnabled(true); + } + if (s.length() > 2 && !isLoadingInstance) { + final String action = "/instances/search"; + final HashMap parameters = new HashMap<>(); + parameters.put("q", s.toString().trim()); + parameters.put("count", String.valueOf(1000)); + parameters.put("name", String.valueOf(true)); + isLoadingInstance = true; + + if (oldSearch == null || !oldSearch.equals(s.toString().trim())) + new Thread(new Runnable() { + @Override + public void run() { + try { + final String response = new HttpsConnection(ReorderTimelinesActivity.this, null).get("https://instances.social/api/1.0" + action, 30, parameters, Helper.THEKINRAR_SECRET_TOKEN); + runOnUiThread(new Runnable() { + public void run() { + isLoadingInstance = false; + String[] instances; + try { + JSONObject jsonObject = new JSONObject(response); + JSONArray jsonArray = jsonObject.getJSONArray("instances"); + if (jsonArray != null) { + int length = 0; + for (int i = 0; i < jsonArray.length(); i++) { + if (!jsonArray.getJSONObject(i).get("name").toString().contains("@") && jsonArray.getJSONObject(i).get("up").toString().equals("true")) + length++; + } + instances = new String[length]; + int j = 0; + for (int i = 0; i < jsonArray.length(); i++) { + if (!jsonArray.getJSONObject(i).get("name").toString().contains("@") && jsonArray.getJSONObject(i).get("up").toString().equals("true")) { + instances[j] = jsonArray.getJSONObject(i).get("name").toString(); + j++; + } + } + } else { + instances = new String[]{}; + } + instance_list.setAdapter(null); + ArrayAdapter adapter = + new ArrayAdapter<>(ReorderTimelinesActivity.this, android.R.layout.simple_list_item_1, instances); + instance_list.setAdapter(adapter); + if (instance_list.hasFocus() && !ReorderTimelinesActivity.this.isFinishing()) + instance_list.showDropDown(); + oldSearch = s.toString().trim(); + + } catch (JSONException ignored) { + isLoadingInstance = false; + } + } + }); + + } catch (HttpsConnection.HttpsConnectionException e) { + isLoadingInstance = false; + } catch (Exception e) { + isLoadingInstance = false; + } + } + }).start(); + else + isLoadingInstance = false; + } + }else { alertDialog.getButton( AlertDialog.BUTTON_POSITIVE).setEnabled(true); } - if (s.length() > 2 && !isLoadingInstance) { - final String action = "/instances/search"; - final HashMap parameters = new HashMap<>(); - parameters.put("q", s.toString().trim()); - parameters.put("count", String.valueOf(1000)); - parameters.put("name", String.valueOf(true)); - isLoadingInstance = true; - if (oldSearch == null || !oldSearch.equals(s.toString().trim())) - new Thread(new Runnable() { - @Override - public void run() { - try { - final String response = new HttpsConnection(ReorderTimelinesActivity.this, null).get("https://instances.social/api/1.0" + action, 30, parameters, Helper.THEKINRAR_SECRET_TOKEN); - runOnUiThread(new Runnable() { - public void run() { - isLoadingInstance = false; - String[] instances; - try { - JSONObject jsonObject = new JSONObject(response); - JSONArray jsonArray = jsonObject.getJSONArray("instances"); - if (jsonArray != null) { - int length = 0; - for (int i = 0; i < jsonArray.length(); i++) { - if (!jsonArray.getJSONObject(i).get("name").toString().contains("@") && jsonArray.getJSONObject(i).get("up").toString().equals("true")) - length++; - } - instances = new String[length]; - int j = 0; - for (int i = 0; i < jsonArray.length(); i++) { - if (!jsonArray.getJSONObject(i).get("name").toString().contains("@") && jsonArray.getJSONObject(i).get("up").toString().equals("true")) { - instances[j] = jsonArray.getJSONObject(i).get("name").toString(); - j++; - } - } - } else { - instances = new String[]{}; - } - instance_list.setAdapter(null); - ArrayAdapter adapter = - new ArrayAdapter<>(ReorderTimelinesActivity.this, android.R.layout.simple_list_item_1, instances); - instance_list.setAdapter(adapter); - if (instance_list.hasFocus() && !ReorderTimelinesActivity.this.isFinishing()) - instance_list.showDropDown(); - oldSearch = s.toString().trim(); - - } catch (JSONException ignored) { - isLoadingInstance = false; - } - } - }); - - } catch (HttpsConnection.HttpsConnectionException e) { - isLoadingInstance = false; - } catch (Exception e) { - isLoadingInstance = false; - } - } - }).start(); - else - isLoadingInstance = false; - } } }); } diff --git a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java index 72658d5b5..4ce798c55 100644 --- a/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java +++ b/app/src/main/java/app/fedilab/android/asynctasks/RetrieveFeedsAsyncTask.java @@ -249,7 +249,15 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { status.setType(action); } } - } else if (remoteInstanceObj != null && remoteInstanceObj.size() > 0 && remoteInstanceObj.get(0).getType().equals("PIXELFED")) { + } else if (remoteInstanceObj != null && remoteInstanceObj.size() > 0 && remoteInstanceObj.get(0).getType().equals("NITTER")) { + apiResponse = api.getNitter(this.instanceName, max_id); + List statusesTemp = apiResponse.getStatuses(); + if (statusesTemp != null) { + for (app.fedilab.android.client.Entities.Status status : statusesTemp) { + status.setType(action); + } + } + }else if (remoteInstanceObj != null && remoteInstanceObj.size() > 0 && remoteInstanceObj.get(0).getType().equals("PIXELFED")) { apiResponse = api.getPixelfedTimeline(instanceName, max_id); } else if (remoteInstanceObj != null && remoteInstanceObj.size() > 0 && remoteInstanceObj.get(0).getType().equals("GNU")) { apiResponse = api.getGNUTimeline(instanceName, max_id); 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 1707b01ef..1832b4aba 100644 --- a/app/src/main/java/app/fedilab/android/client/API.java +++ b/app/src/main/java/app/fedilab/android/client/API.java @@ -19,6 +19,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; +import android.util.Log; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -28,15 +29,21 @@ import com.google.gson.JsonObject; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.text.DateFormat; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -45,6 +52,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import app.fedilab.android.R; import app.fedilab.android.activities.MainActivity; @@ -760,6 +769,147 @@ public class API { return status; } + + /** + * Parse xml response for Nitter + * + * @param xml String + * @return List + */ + + private List parseNitter(String xml) { + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String nitterHost = sharedpreferences.getString(Helper.SET_NITTER_HOST, Helper.DEFAULT_NITTER_HOST).toLowerCase(); + + List statuses = new ArrayList<>(); + try { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(true); + XmlPullParser xpp = factory.newPullParser(); + + xpp.setInput( new StringReader( xml ) ); + int eventType = xpp.getEventType(); + Account account = null; + Status status = null; + HashMap mappedProfile = new HashMap<>(); + while (eventType != XmlPullParser.END_DOCUMENT) { + + if(eventType == XmlPullParser.START_TAG) { + if( xpp.getName().compareTo("item") == 0 ){ + status = new Status(); + status.setReplies_count(0); + status.setFavourites_count(0); + status.setReblogs_count(0); + status.setFavourited(false); + status.setReblogged(false); + status.setReblog(null); + status.setMedia_attachments(new ArrayList<>()); + status.setMentions(new ArrayList<>()); + status.setTags(new ArrayList<>()); + status.setEmojis(new ArrayList<>()); + status.setEmojiFound(true); + status.setImageFound(true); + status.setPollEmojiFound(true); + status.setEmojiTranslateFound(true); + status.setItemViewType(1); + account = new Account(); + }else if( xpp.getName().compareTo("creator") == 0 ){ + eventType = xpp.next(); + if(eventType == XmlPullParser.TEXT) { + if( account != null ){ + account.setAcct(xpp.getText().replace("@","")); + account.setDisplay_name(xpp.getText().replace("@","")); + account.setUsername(xpp.getText().replace("@","")); + account.setId("https://" + nitterHost + "/" + xpp.getText()); + account.setUuid("https://" + nitterHost + "/" + xpp.getText()); + account.setUrl("https://" + nitterHost + "/" + xpp.getText()); + if( !mappedProfile.containsKey(xpp.getText()) ){ + HttpsConnection httpsConnection = new HttpsConnection(context, nitterHost); + try { + String response = httpsConnection.get("https://" + nitterHost + "/" + xpp.getText() + "/rss", 10, null, null); + XmlPullParserFactory factory2 = XmlPullParserFactory.newInstance(); + factory2.setNamespaceAware(true); + XmlPullParser xpp2 = factory2.newPullParser(); + + xpp2.setInput( new StringReader( response ) ); + int eventType2 = xpp2.getEventType(); + while (eventType2 != XmlPullParser.END_DOCUMENT) { + if (eventType2 == XmlPullParser.START_TAG) { + if (xpp2.getName().compareTo("url") == 0) { + eventType2 = xpp2.next(); + if(eventType2 == XmlPullParser.TEXT ) { + mappedProfile.put(xpp.getText(), xpp2.getText()); + } + break; + } + } + eventType2 = xpp2.next(); + } + } catch (NoSuchAlgorithmException | KeyManagementException | HttpsConnection.HttpsConnectionException e) { + e.printStackTrace(); + } + } + account.setAvatar(mappedProfile.get(xpp.getText())); + } + } + } else if( xpp.getName().compareTo("pubDate") == 0 ){ + eventType = xpp.next(); + if(eventType == XmlPullParser.TEXT && status != null) { + if( xpp.getText() != null ) { + try { + DateFormat formatter = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); + Date date = formatter.parse(xpp.getText()); + status.setCreated_at(date); + } catch (ParseException e) { + status.setCreated_at(new Date()); + } + } + } + }else if( xpp.getName().compareTo("description") == 0 ){ + eventType = xpp.next(); + if(eventType == XmlPullParser.TEXT && status != null) { + if( xpp.getText() != null ) { + status.setContent(context, xpp.getText()); + } + } + }else if( xpp.getName().compareTo("guid") == 0 ){ + eventType = xpp.next(); + if(eventType == XmlPullParser.TEXT && status != null) { + if( xpp.getText() != null ) { + status.setUri(xpp.getText()); + Pattern idPattern = Pattern.compile("([0-9])+"); + Matcher matcher = idPattern.matcher(xpp.getText()); + while (matcher.find()) { + status.setId(matcher.group(0)); + } + } + } + }else if( xpp.getName().compareTo("link") == 0 ){ + eventType = xpp.next(); + if(eventType == XmlPullParser.TEXT && status != null) { + if( xpp.getText() != null ) { + status.setUrl(xpp.getText()); + } + } + } + } else if(eventType == XmlPullParser.END_TAG) { + if( xpp.getName().compareTo("item") == 0 ){ + if (status != null) { + status.setAccount(account); + statuses.add(status); + } + account = null; + status = null; + } + } + eventType = xpp.next(); + } + } catch (XmlPullParserException | IOException e) { + e.printStackTrace(); + } + return statuses; + } + /** * Parse json response for several notes (Misskey) * @@ -3237,8 +3387,45 @@ public class API { return apiResponse; } + /** - * Retrieves Peertube videos from an instance *synchronously* + * Retrieves Nitter timeline from accounts *synchronously* + * + * @return APIResponse + */ + public APIResponse getNitter(String instance, String max_id) { + + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String nitterHost = sharedpreferences.getString(Helper.SET_NITTER_HOST, Helper.DEFAULT_NITTER_HOST).toLowerCase(); + String[] usernames = instance.split(" "); + if( usernames.length == 0 ){ + Error error = new Error(); + error.setError(context.getString(R.string.toast_error)); + error.setStatusCode(404); + apiResponse.setError(error); + return apiResponse; + } + StringBuilder params = new StringBuilder(); + for(String param: usernames){ + params.append(param.trim()).append(","); + } + try { + statuses = new ArrayList<>(); + HttpsConnection httpsConnection = new HttpsConnection(context, this.instance); + String response = httpsConnection.get("https://" + nitterHost + "/" + params + "/rss", 10, null, null); + statuses = parseNitter(response); + + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException e) { + e.printStackTrace(); + } + apiResponse.setStatuses(statuses); + return apiResponse; + } + + /** + * Retrieves Misskey timeline from an instance *synchronously* * * @return APIResponse */ diff --git a/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java b/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java index c5e974427..972c02926 100644 --- a/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java +++ b/app/src/main/java/app/fedilab/android/drawers/StatusListAdapter.java @@ -227,6 +227,7 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct private List lstHolders; private List emojisPicker; private Status statusForQuickReply; + private String instanceType; private Runnable updateAnimatedEmoji = new Runnable() { @Override @@ -266,6 +267,21 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct currentToId = -1; } + public StatusListAdapter(String instanceType, RetrieveFeedsAsyncTask.Type type, String targetedId, boolean isOnWifi, List statuses) { + super(); + this.statuses = statuses; + this.isOnWifi = isOnWifi; + statusListAdapter = this; + this.type = type; + this.targetedId = targetedId; + redraft = false; + lstHolders = new ArrayList<>(); + toot_content = null; + toot_cw_content = null; + tootReply = null; + currentToId = -1; + this.instanceType = instanceType; + } public StatusListAdapter(TagTimeline tagTimeline, String targetedId, boolean isOnWifi, List statuses) { super(); @@ -283,6 +299,23 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct currentToId = -1; } + public StatusListAdapter(String instanceType, TagTimeline tagTimeline, String targetedId, boolean isOnWifi, List statuses) { + super(); + this.statuses = statuses; + this.isOnWifi = isOnWifi; + statusListAdapter = this; + this.type = RetrieveFeedsAsyncTask.Type.TAG; + this.targetedId = targetedId; + redraft = false; + this.tagTimeline = tagTimeline; + this.instanceType = instanceType; + lstHolders = new ArrayList<>(); + toot_content = null; + toot_cw_content = null; + tootReply = null; + currentToId = -1; + } + public StatusListAdapter(int position, String targetedId, boolean isOnWifi, List statuses) { this.statuses = statuses; this.isOnWifi = isOnWifi; @@ -616,15 +649,20 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct show_boosts = ((ShowAccountActivity) context).showBoosts(); show_replies = ((ShowAccountActivity) context).showReplies(); } - if (type != RetrieveFeedsAsyncTask.Type.REMOTE_INSTANCE && type != RetrieveFeedsAsyncTask.Type.NEWS && !Helper.filterToots(statuses.get(position), type, context instanceof ShowAccountActivity, show_boosts, show_replies)) + if (type != RetrieveFeedsAsyncTask.Type.REMOTE_INSTANCE && type != RetrieveFeedsAsyncTask.Type.NEWS && !Helper.filterToots(statuses.get(position), type, context instanceof ShowAccountActivity, show_boosts, show_replies)) { return HIDDEN_STATUS; + } if (statuses.get(position).isFocused() && type == RetrieveFeedsAsyncTask.Type.CONTEXT && statuses.get(position).getViewType() != CONSOLE_STATUS) return FOCUSED_STATUS; else { if (social == UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED && type == RetrieveFeedsAsyncTask.Type.CONTEXT) { return COMPACT_STATUS; } else { - return statuses.get(position).getViewType(); + if( instanceType == null || instanceType.compareTo("NITTER") != 0 ) { + return statuses.get(position).getViewType(); + }else{ + return COMPACT_STATUS; + } } } } @@ -653,6 +691,7 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); final String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); context = viewHolder.itemView.getContext(); + if (viewHolder.getItemViewType() != HIDDEN_STATUS) { final ViewHolder holder = (ViewHolder) viewHolder; synchronized (lock) { @@ -662,9 +701,10 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct holder.startUpdateTimer(); final Status status = statuses.get(i); + + if (status == null) return; - //TODO:It sounds that sometimes this value is null - need deeper investigation if (status.getVisibility() == null) { status.setVisibility("public"); @@ -1301,6 +1341,9 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct status.setImageFound(true); Status.makeImage(context, this, status); } + if (instanceType != null && instanceType.compareTo("NITTER") == 0) { + holder.status_action_container.setVisibility(View.GONE); + } holder.status_content.setOnTouchListener(new View.OnTouchListener() { @Override @@ -3225,6 +3268,7 @@ public class StatusListAdapter extends RecyclerView.Adapter implements OnPostAct } } + } private void loadAttachments(final Status status, final ViewHolder holder, boolean blur) { diff --git a/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java b/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java index 079ef4f0e..c008b0a42 100644 --- a/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java +++ b/app/src/main/java/app/fedilab/android/fragments/DisplayStatusFragment.java @@ -206,7 +206,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn lastReadToot = sharedpreferences.getString(Helper.LAST_READ_TOOT_ID + userId + instance, null); lastReadTootDate = Helper.stringToDate(context, sharedpreferences.getString(Helper.LAST_READ_TOOT_DATE + userId + instance, null)); } - if (instanceType == null || instanceType.equals("MASTODON") || instanceType.equals("MISSKEY") || instanceType.equals("GNU")) { + if (instanceType == null || instanceType.equals("MASTODON") || instanceType.equals("MISSKEY") || instanceType.equals("GNU") || instanceType.equals("NITTER")) { if (type == RetrieveFeedsAsyncTask.Type.TAG && tag != null) { BaseMainActivity.displayPeertube = null; List tagTimelines = new SearchDAO(context, db).getTimelineInfo(tag); @@ -217,7 +217,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn } } else { BaseMainActivity.displayPeertube = null; - statusListAdapter = new StatusListAdapter(type, targetedId, isOnWifi, this.statuses); + statusListAdapter = new StatusListAdapter(instanceType, type, targetedId, isOnWifi, this.statuses); lv_status.setAdapter(statusListAdapter); } } else if (instanceType.equals("PEERTUBE")) { @@ -580,7 +580,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn } //Let's deal with statuses if (statuses != null && statuses.size() > 0) { - if (statusListAdapter != null && (instanceType.equals("MASTODON") || instanceType.equals("MISSKEY") || instanceType.equals("GNU"))) { + if (statusListAdapter != null && (instanceType.equals("MASTODON") || instanceType.equals("NITTER") || instanceType.equals("MISSKEY") || instanceType.equals("GNU"))) { this.statuses.addAll(statuses); statusListAdapter.notifyItemRangeInserted(previousPosition, statuses.size()); } else if (artListAdapter != null && instanceType.equals("ART")) { @@ -863,6 +863,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn switch (instanceType) { case "MASTODON": case "MISSKEY": + case "NITTER": case "GNU": statusListAdapter.notifyItemRangeChanged(0, this.statuses.size()); break; @@ -892,7 +893,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn Status status = it.next(); for (Status status1 : statuses) { if (status.getConversationId() != null && status.getConversationId().equals(status1.getConversationId())) { - if (instanceType.equals("MASTODON") || instanceType.equals("MISSKEY") || instanceType.equals("GNU")) + if (instanceType.equals("MASTODON") || instanceType.equals("MISSKEY") || instanceType.equals("NITTER")|| instanceType.equals("GNU")) statusListAdapter.notifyItemRemoved(position); else if (instanceType.equals("PIXELFED")) pixelfedListAdapter.notifyItemRemoved(position); @@ -931,7 +932,8 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn } } } - if (statusListAdapter != null && (instanceType.equals("MASTODON") || instanceType.equals("MISSKEY") || instanceType.equals("GNU"))) + + if (statusListAdapter != null && (instanceType.equals("MASTODON") || instanceType.equals("MISSKEY") || instanceType.equals("NITTER") || instanceType.equals("GNU"))) statusListAdapter.notifyItemRangeInserted(0, inserted); else if (pixelfedListAdapter != null && instanceType.equals("PIXELFED")) pixelfedListAdapter.notifyItemRangeInserted(0, inserted); @@ -1158,6 +1160,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn switch (instanceType) { case "MASTODON": case "MISSKEY": + case "NITTER": case "GNU": statusListAdapter.notifyItemRangeRemoved(0, lenght); break;