mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2025-07-14 15:40:29 +03:00
Fix Nitter instances
This commit is contained in:
parent
fae30e63a8
commit
8d3621dbe7
6 changed files with 197 additions and 132 deletions
|
@ -14,10 +14,12 @@ package app.fedilab.android.mastodon.client.entities.nitter;
|
|||
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.jsoup.select.Elements;
|
||||
import org.simpleframework.xml.Element;
|
||||
import org.simpleframework.xml.ElementList;
|
||||
import org.simpleframework.xml.Namespace;
|
||||
|
@ -29,6 +31,7 @@ import java.net.IDN;
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -82,7 +85,8 @@ public class Nitter implements Serializable {
|
|||
status.text = feedItem.title;
|
||||
status.content = status.content.replaceAll("<img [^>]*src=\"[^\"]+\"[^>]*>", "");
|
||||
status.visibility = "public";
|
||||
status.created_at = Helper.stringToDateWithFormat(context, feedItem.pubDate, "EEE, dd MMM yyyy HH:mm:ss zzz");
|
||||
String dateFormat = "E', 'dd' 'MMM' 'yyyy' 'hh:m:s' GMT'";
|
||||
status.created_at = Helper.stringToDateWithFormat(context, feedItem.pubDate, dateFormat);
|
||||
status.uri = feedItem.guid;
|
||||
status.url = feedItem.link;
|
||||
if (!accounts.containsKey(feedItem.creator)) {
|
||||
|
@ -177,4 +181,83 @@ public class Nitter implements Serializable {
|
|||
}
|
||||
|
||||
|
||||
public static Status nitterHTMLParser(Context context, org.jsoup.nodes.Element timelineItem, String nitterInstance) {
|
||||
|
||||
if(timelineItem == null) {
|
||||
return null;
|
||||
}
|
||||
Status status = new Status();
|
||||
Account account = new Account();
|
||||
String fedilabInstance = "nitter.fedilab.app";
|
||||
|
||||
|
||||
org.jsoup.nodes.Element messageLink;
|
||||
if(timelineItem.select(".quote-text").html().isEmpty()) {
|
||||
status.content = timelineItem.select(".tweet-content").html();
|
||||
status.text = timelineItem.select(".tweet-content").text();
|
||||
status.url = "https://"+ nitterInstance +timelineItem.select(".tweet-link").attr("href");
|
||||
messageLink = timelineItem.select(".tweet-link").first();
|
||||
} else {
|
||||
status.content = timelineItem.select(".quote-text").html();
|
||||
status.text = timelineItem.select(".quote-text").text();
|
||||
status.url = "https://"+ nitterInstance +timelineItem.select(".quote-link").attr("href");
|
||||
messageLink = timelineItem.select(".quote-link").first();
|
||||
}
|
||||
status.uri = status.url;
|
||||
|
||||
String status_id = String.valueOf(ThreadLocalRandom.current().nextLong(10,10000000));;
|
||||
if(messageLink != null){
|
||||
String[] splitLink = messageLink.attr("href").split("/");
|
||||
status_id = splitLink[splitLink.length-1];
|
||||
}
|
||||
String pubDate = timelineItem.select(".tweet-date").select("a").attr("title");
|
||||
org.jsoup.nodes.Element nameElement = timelineItem.select(".fullname").first();
|
||||
String name = nameElement!= null?nameElement.text():"";
|
||||
org.jsoup.nodes.Element userNameElement = timelineItem.select(".username").first();
|
||||
String userName = userNameElement!= null?userNameElement.text().replace("@",""):"";
|
||||
String avatar = "https://" + fedilabInstance + timelineItem.select(".avatar").attr("src");
|
||||
account.id = userName;
|
||||
account.acct = userName;
|
||||
if(timelineItem.select(".replying-to").html().isEmpty()) {
|
||||
account.username = userName;
|
||||
account.display_name = name;
|
||||
} else {
|
||||
account.display_name = timelineItem.select(".fullname").text() +" " +timelineItem.select(".replying-to").text();
|
||||
}
|
||||
|
||||
account.avatar = avatar;
|
||||
account.avatar_static = avatar;
|
||||
account.url = "https://"+ nitterInstance +"/" + userName;
|
||||
status.id = status_id;
|
||||
status.account = account;
|
||||
|
||||
|
||||
|
||||
Elements imageElements = timelineItem.select(".attachments").select("img");
|
||||
Elements videoElements = timelineItem.select(".attachments").select("video");
|
||||
ArrayList<Attachment> attachmentList = new ArrayList<>();
|
||||
for(org.jsoup.nodes.Element imageElement: imageElements) {
|
||||
Attachment attachment = new Attachment();
|
||||
attachment.type = "image";
|
||||
attachment.url = "https://"+fedilabInstance+imageElement.attr("src");
|
||||
attachment.preview_url = "https://"+fedilabInstance+imageElement.attr("src");
|
||||
attachment.id = imageElement.attr("src");
|
||||
attachmentList.add(attachment);
|
||||
}
|
||||
for(org.jsoup.nodes.Element videoElement: videoElements) {
|
||||
Attachment attachment = new Attachment();
|
||||
attachment.type = "video";
|
||||
attachment.url = "https://"+fedilabInstance+videoElement.child(0).attr("src");
|
||||
attachment.preview_url = "https://"+fedilabInstance+videoElement.attr("poster");
|
||||
attachment.id = videoElement.attr("poster");
|
||||
attachmentList.add(attachment);
|
||||
}
|
||||
status.visibility = "public";
|
||||
status.media_attachments = attachmentList;
|
||||
String dateFormat = "MMM d', 'yyyy' · 'h:m a' UTC'";
|
||||
status.created_at = Helper.stringToDateWithFormat(context, pubDate, dateFormat);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@ import android.provider.OpenableColumns;
|
|||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
@ -647,9 +646,9 @@ public class Helper {
|
|||
*/
|
||||
public static Date stringToDateWithFormat(Context context, String stringDate, String format) {
|
||||
if (stringDate == null)
|
||||
return null;
|
||||
return new Date();
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(format, Locale.US);
|
||||
Date date = null;
|
||||
Date date = new Date();
|
||||
try {
|
||||
date = dateFormat.parse(stringDate);
|
||||
} catch (java.text.ParseException ignored) {
|
||||
|
|
|
@ -58,7 +58,6 @@ import android.os.Looper;
|
|||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
|
@ -567,15 +566,19 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
}
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(context, ContextActivity.class);
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(Helper.ARG_STATUS, statusToDeal.quote);
|
||||
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
|
||||
intent.putExtras(bundle);
|
||||
context.startActivity(intent);
|
||||
});
|
||||
if (!remote) {
|
||||
Intent intent = new Intent(context, ContextActivity.class);
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(Helper.ARG_STATUS, statusToDeal.quote);
|
||||
new CachedBundle(context).insertBundle(args, Helper.getCurrentAccount(context), bundleId -> {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong(Helper.ARG_INTENT_ID, bundleId);
|
||||
intent.putExtras(bundle);
|
||||
context.startActivity(intent);
|
||||
});
|
||||
} else {
|
||||
Helper.openBrowser(context,statusToDeal.quote.url);
|
||||
}
|
||||
});
|
||||
holder.binding.quotedMessage.cardviewContainer.setStrokeColor(ThemeHelper.getAttColor(context, R.attr.colorPrimary));
|
||||
holder.binding.quotedMessage.statusContent.setText(
|
||||
|
@ -1453,15 +1456,24 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
|||
|
||||
//--- BOOSTER INFO ---
|
||||
if (status.reblog != null) {
|
||||
MastodonHelper.loadPPMastodon(holder.binding.statusBoosterAvatar, status.account);
|
||||
|
||||
if(status.account.avatar != null) {
|
||||
MastodonHelper.loadPPMastodon(holder.binding.statusBoosterAvatar, status.account);
|
||||
holder.binding.statusBoosterAvatar.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.binding.statusBoosterAvatar.setVisibility(View.GONE);
|
||||
}
|
||||
holder.binding.statusBoosterDisplayName.setText(
|
||||
status.account.getSpanDisplayName(context,
|
||||
new WeakReference<>(holder.binding.statusBoosterDisplayName)),
|
||||
TextView.BufferType.SPANNABLE);
|
||||
|
||||
holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE);
|
||||
holder.binding.statusBoosterUsername.setText(String.format("@%s", status.account.acct));
|
||||
if(status.account.acct != null) {
|
||||
holder.binding.statusBoosterUsername.setText(String.format("@%s", status.account.acct));
|
||||
holder.binding.statusBoosterUsername.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.binding.statusBoosterUsername.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
holder.binding.statusBoosterInfo.setVisibility(View.GONE);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package app.fedilab.android.mastodon.ui.fragment.timeline;
|
|||
|
||||
import static app.fedilab.android.BaseMainActivity.currentInstance;
|
||||
import static app.fedilab.android.BaseMainActivity.networkAvailable;
|
||||
import static app.fedilab.android.mastodon.helper.Helper.TAG;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
|
@ -418,7 +419,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
search = bundle.getString(Helper.ARG_SEARCH_KEYWORD, null);
|
||||
searchCache = bundle.getString(Helper.ARG_SEARCH_KEYWORD_CACHE, null);
|
||||
pinnedTimeline = (PinnedTimeline) bundle.getSerializable(Helper.ARG_REMOTE_INSTANCE);
|
||||
|
||||
canBeFederated = true;
|
||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null) {
|
||||
if (pinnedTimeline.remoteInstance.type != RemoteInstance.InstanceType.NITTER) {
|
||||
remoteInstance = pinnedTimeline.remoteInstance.host;
|
||||
|
@ -490,7 +491,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
initialStatuses = null;
|
||||
lockForResumeCall = 0;
|
||||
|
||||
canBeFederated = true;
|
||||
|
||||
retry_for_home_done = false;
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
|
||||
boolean displayScrollBar = sharedpreferences.getBoolean(getString(R.string.SET_TIMELINE_SCROLLBAR), false);
|
||||
|
@ -562,7 +563,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
//Update the timeline with new statuses
|
||||
int insertedStatus;
|
||||
if (timelineType != Timeline.TimeLineEnum.TREND_MESSAGE_PUBLIC && timelineType != Timeline.TimeLineEnum.TREND_MESSAGE ) {
|
||||
if(pinnedTimeline!= null && pinnedTimeline.remoteInstance != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) {
|
||||
insertedStatus = fetched_statuses.statuses.size();
|
||||
int fromPosition = timelineStatuses.size();
|
||||
timelineStatuses.addAll(fetched_statuses.statuses);
|
||||
statusAdapter.notifyItemRangeInserted(fromPosition, insertedStatus);
|
||||
} else if (timelineType != Timeline.TimeLineEnum.TREND_MESSAGE_PUBLIC && timelineType != Timeline.TimeLineEnum.TREND_MESSAGE ) {
|
||||
insertedStatus = updateStatusListWith(fetched_statuses.statuses);
|
||||
} else { //Trends cannot be ordered by id
|
||||
insertedStatus = fetched_statuses.statuses.size();
|
||||
|
@ -679,8 +685,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
if (max_id == null || (statuses.pagination.max_id != null && Helper.compareTo(statuses.pagination.max_id, max_id) < 0) || timelineType.getValue().startsWith("TREND_")) {
|
||||
max_id = statuses.pagination.max_id;
|
||||
}
|
||||
//For Lemmy pagination
|
||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.LEMMY) {
|
||||
//For Lemmy and Nitter pagination
|
||||
if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null && (pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.LEMMY || pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER)) {
|
||||
max_id = statuses.pagination.max_id;
|
||||
}
|
||||
if (min_id == null || (statuses.pagination.min_id != null && Helper.compareTo(statuses.pagination.min_id, min_id) > 0)) {
|
||||
|
@ -1066,6 +1072,26 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
});
|
||||
}
|
||||
}//LEMMY TIMELINES
|
||||
else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.LEMMY) {
|
||||
if (direction == null) {
|
||||
timelinesVM.getLemmy(remoteInstance, lemmy_post_id, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getLemmy(remoteInstance, lemmy_post_id, max_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
flagLoading = false;
|
||||
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
|
||||
timelinesVM.getLemmy(remoteInstance, lemmy_post_id, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesRefresh -> {
|
||||
if (statusAdapter != null) {
|
||||
dealWithPagination(statusesRefresh, direction, true, true, fetchStatus);
|
||||
} else {
|
||||
initializeStatusesCommonView(statusesRefresh);
|
||||
}
|
||||
});
|
||||
}
|
||||
}//MISSKEY TIMELINES
|
||||
else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.MISSKEY) {
|
||||
if (direction == null) {
|
||||
|
@ -1087,27 +1113,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
|
|||
}
|
||||
});
|
||||
}
|
||||
} //LEMMY TIMELINES
|
||||
else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.LEMMY) {
|
||||
if (direction == null) {
|
||||
timelinesVM.getLemmy(remoteInstance, lemmy_post_id, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), this::initializeStatusesCommonView);
|
||||
} else if (direction == DIRECTION.BOTTOM) {
|
||||
timelinesVM.getLemmy(remoteInstance, lemmy_post_id, max_id, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false, true, fetchStatus));
|
||||
} else if (direction == DIRECTION.TOP) {
|
||||
flagLoading = false;
|
||||
} else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
|
||||
timelinesVM.getLemmy(remoteInstance, lemmy_post_id, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
.observe(getViewLifecycleOwner(), statusesRefresh -> {
|
||||
if (statusAdapter != null) {
|
||||
dealWithPagination(statusesRefresh, direction, true, true, fetchStatus);
|
||||
} else {
|
||||
initializeStatusesCommonView(statusesRefresh);
|
||||
}
|
||||
});
|
||||
}
|
||||
}//PEERTUBE TIMELINES
|
||||
} //PEERTUBE TIMELINES
|
||||
else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.PEERTUBE) {
|
||||
if (direction == null) {
|
||||
timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity()))
|
||||
|
|
|
@ -41,15 +41,12 @@ import java.net.IDN;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import app.fedilab.android.BuildConfig;
|
||||
import app.fedilab.android.R;
|
||||
import app.fedilab.android.activities.MainActivity;
|
||||
import app.fedilab.android.mastodon.client.endpoints.MastodonTimelinesService;
|
||||
import app.fedilab.android.mastodon.client.entities.api.Account;
|
||||
import app.fedilab.android.mastodon.client.entities.api.Attachment;
|
||||
import app.fedilab.android.mastodon.client.entities.api.Conversation;
|
||||
import app.fedilab.android.mastodon.client.entities.api.Conversations;
|
||||
import app.fedilab.android.mastodon.client.entities.api.Marker;
|
||||
|
@ -255,19 +252,12 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
public LiveData<Statuses> getNitterRSS(
|
||||
String accountsStr,
|
||||
String max_position) {
|
||||
Context context = getApplication().getApplicationContext();
|
||||
SharedPreferences sharedpreferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
String instance = sharedpreferences.getString(context.getString(R.string.SET_NITTER_HOST), context.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
||||
if (instance.trim().isEmpty()) {
|
||||
instance = context.getString(R.string.DEFAULT_NITTER_HOST);
|
||||
}
|
||||
MastodonTimelinesService mastodonTimelinesService = initInstanceXMLOnly(instance);
|
||||
MastodonTimelinesService mastodonTimelinesService = initInstanceXMLOnly("nitter.fedilab.app");
|
||||
accountsStr = accountsStr.replaceAll("\\s", ",");
|
||||
|
||||
statusesMutableLiveData = new MutableLiveData<>();
|
||||
String finalAccountsStr = accountsStr;
|
||||
String finalInstance = instance;
|
||||
|
||||
new Thread(() -> {
|
||||
Call<Nitter> publicTlCall = mastodonTimelinesService.getNitter(finalAccountsStr, max_position);
|
||||
Statuses statuses = new Statuses();
|
||||
|
@ -280,7 +270,7 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
if (rssResponse != null && rssResponse.mFeedItems != null) {
|
||||
for (Nitter.FeedItem feedItem : rssResponse.mFeedItems) {
|
||||
if (!feedItem.title.startsWith("RT by")) {
|
||||
Status status = Nitter.convert(getApplication(), finalInstance, feedItem);
|
||||
Status status = Nitter.convert(getApplication(), "nitter.fedilab.app", feedItem);
|
||||
statusList.add(status);
|
||||
}
|
||||
}
|
||||
|
@ -314,99 +304,75 @@ public class TimelinesVM extends AndroidViewModel {
|
|||
String max_position) {
|
||||
statusesMutableLiveData = new MutableLiveData<>();
|
||||
Context context = getApplication().getApplicationContext();
|
||||
SharedPreferences sharedpreferences = PreferenceManager
|
||||
.getDefaultSharedPreferences(context);
|
||||
String instance = sharedpreferences.getString(context.getString(R.string.SET_NITTER_HOST), context.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
||||
if (instance.trim().isEmpty()) {
|
||||
instance = context.getString(R.string.DEFAULT_NITTER_HOST);
|
||||
}
|
||||
//TODO: remove after tests
|
||||
instance = "nitter.privacydev.net";
|
||||
|
||||
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String nitterInstance = sharedpreferences.getString(context.getString(R.string.SET_NITTER_HOST), context.getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase();
|
||||
final String fedilabInstance = "nitter.fedilab.app";
|
||||
accountsStr = accountsStr.replaceAll("\\s", ",").replaceAll(",,",",");
|
||||
String maxposition = max_position == null ? "" : "?max_position="+max_position;
|
||||
String url = "https://" + instance + "/" + accountsStr + "/with_replies"+maxposition;
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
|
||||
.writeTimeout(10, TimeUnit.SECONDS)
|
||||
.readTimeout(10, TimeUnit.SECONDS).build();
|
||||
String cursor = max_position == null ? "" : max_position;
|
||||
String url = "https://"+fedilabInstance+"/" + accountsStr + "/with_replies" +cursor;
|
||||
Request request = new Request.Builder()
|
||||
.header("User-Agent", context.getString(R.string.app_name) + "/" + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE)
|
||||
.url(url)
|
||||
.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||
.header("accept-language","en-US;q=0.6")
|
||||
.header("dnt","1")
|
||||
.header("user-agent","Mozilla/5.0 (X11; Linux i686; rv:135.0) Gecko/20100101 Firefox/135.0")
|
||||
.get()
|
||||
.build();
|
||||
String finalInstance = instance;
|
||||
String finalInstance1 = instance;
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
|
||||
okHttpClient.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(@NonNull okhttp3.Call call, @NonNull okhttp3.Response response) throws IOException {
|
||||
|
||||
Statuses statuses = new Statuses();
|
||||
|
||||
if (response.isSuccessful()) {
|
||||
try {
|
||||
String data = response.body().string();
|
||||
|
||||
Document doc = Jsoup.parse(data);
|
||||
Elements timelineItems = doc.select(".timeline-item");
|
||||
|
||||
List<Status> statusList = new ArrayList<>();
|
||||
for(Element timelineItem: timelineItems) {
|
||||
|
||||
//Not a RT
|
||||
if(timelineItem.select(".icon-retweet").html().trim().isEmpty()) {
|
||||
Status status = new Status();
|
||||
Account account = new Account();
|
||||
|
||||
String[] splitLink = timelineItem.select(".tweet-link").text().split("/");
|
||||
String status_id = splitLink[splitLink.length-1];
|
||||
String pubDate = timelineItem.select(".tweet-date").select("a").attr("title");
|
||||
String name = timelineItem.select(".fullname").text();
|
||||
String userName = timelineItem.select(".username").text();
|
||||
String avatar = "https://"+ finalInstance + timelineItem.select(".avatar").attr("src");
|
||||
account.id = userName;
|
||||
account.acct = userName;
|
||||
account.username = userName;
|
||||
account.display_name = name;
|
||||
account.avatar = avatar;
|
||||
account.avatar_static = avatar;
|
||||
account.url = "https://"+ finalInstance +"/" + userName;
|
||||
|
||||
status.id = status_id;
|
||||
status.account = account;
|
||||
status.url = "https://"+ finalInstance +timelineItem.select(".tweet-link").attr("href");
|
||||
status.content = timelineItem.select(".tweet-content").text();
|
||||
Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>");
|
||||
Matcher matcher = imgPattern.matcher(status.content);
|
||||
String description = status.content;
|
||||
ArrayList<Attachment> attachmentList = new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
description = description.replaceAll(Pattern.quote(matcher.group()), "");
|
||||
Attachment attachment = new Attachment();
|
||||
attachment.type = "image";
|
||||
attachment.url = matcher.group(1);
|
||||
attachment.preview_url = matcher.group(1);
|
||||
attachment.id = matcher.group(1);
|
||||
attachmentList.add(attachment);
|
||||
}
|
||||
status.visibility = "public";
|
||||
status.media_attachments = attachmentList;
|
||||
String dateformat = "MMM d', 'yyyy' · 'h:m a' UTC'";
|
||||
status.created_at = Helper.stringToDateWithFormat(context, pubDate, dateformat);
|
||||
statusList.add(status);
|
||||
if(!timelineItem.select(".unavailable").html().isEmpty() || timelineItem.select(".tweet-link").attr("href").isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
//RT
|
||||
boolean isBoosted = !timelineItem.select(".retweet-header").select(".icon-container").isEmpty();
|
||||
Status status = Nitter.nitterHTMLParser(context, timelineItem, nitterInstance);
|
||||
|
||||
//Quoted message
|
||||
|
||||
if(!timelineItem.select(".quote").html().isEmpty()) {
|
||||
status.quote = Nitter.nitterHTMLParser(context, timelineItem.select(".quote").first(), nitterInstance);
|
||||
}
|
||||
|
||||
Status finalStatus;
|
||||
if(isBoosted) {
|
||||
finalStatus = new Status();
|
||||
finalStatus.reblog = status;
|
||||
finalStatus.id = status.id;
|
||||
finalStatus.visibility = "public";
|
||||
finalStatus.url = "https://"+ nitterInstance +timelineItem.select(".tweet-link").attr("href");
|
||||
finalStatus.uri = finalStatus.url;
|
||||
Account acccountOriginal = new Account();
|
||||
acccountOriginal.display_name = timelineItem.select(".retweet-header").select(".icon-container").text();
|
||||
finalStatus.account = acccountOriginal;
|
||||
} else {
|
||||
finalStatus = status;
|
||||
}
|
||||
statusList.add(finalStatus);
|
||||
}
|
||||
|
||||
statuses.statuses = statusList;
|
||||
String max_id = response.headers().get("min-id");
|
||||
Elements elementsShow = doc.select(".show-more a");
|
||||
Element showMore = null;
|
||||
if(elementsShow.size() > 1) {
|
||||
showMore = elementsShow.get(elementsShow.size()-1);
|
||||
} else {
|
||||
showMore = elementsShow.get(0);
|
||||
}
|
||||
String cursor = showMore.attr("href");
|
||||
statuses.pagination = new Pagination();
|
||||
statuses.pagination.max_id = max_id;
|
||||
statuses.pagination.max_id = cursor;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
|
@ -612,7 +612,7 @@
|
|||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:id="@+id/action_share_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="28dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="48dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
|
@ -625,12 +625,11 @@
|
|||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/action_share"
|
||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:iconGravity="textStart"
|
||||
android:adjustViewBounds="true"
|
||||
app:icon="@drawable/ic_baseline_share_24"
|
||||
app:iconGravity="textStart"
|
||||
app:iconPadding="0dp"
|
||||
app:strokeColor="?colorPrimary" />
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
|
|
Loading…
Reference in a new issue