diff options
author | Thomas <tschneider.ac@gmail.com> | 2022-06-27 18:16:41 +0200 |
---|---|---|
committer | Thomas <tschneider.ac@gmail.com> | 2022-06-27 18:16:41 +0200 |
commit | 79d037036c1847ad2fc5b154de7b89b05236ea6d (patch) | |
tree | cf9bd8ccae892a2179fdd592ac83ec4d1070a82e | |
parent | e32101a6bb73558255368e31ef22b2f6ccd3b027 (diff) |
Follow Peertube & Misskey instances
8 files changed, 639 insertions, 48 deletions
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 cde71cdf2..90a6c0cc0 100644 --- a/app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java @@ -19,6 +19,7 @@ import static app.fedilab.android.helper.PinnedTimelineHelper.sortMenuItem; import static app.fedilab.android.helper.PinnedTimelineHelper.sortPositionAsc; import android.content.Intent; +import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.Handler; @@ -37,6 +38,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.lifecycle.ViewModelProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -69,8 +71,10 @@ import app.fedilab.android.viewmodel.mastodon.ReorderVM; import es.dmoral.toasty.Toasty; import okhttp3.Call; import okhttp3.Callback; +import okhttp3.FormBody; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; @@ -87,7 +91,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra private ActivityReorderTabsBinding binding; private boolean changes; private boolean bottomChanges; - + private boolean update; public void setChanges(boolean changes) { this.changes = changes; } @@ -112,10 +116,12 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra bottomChanges = false; ReorderVM reorderVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(ReorderVM.class); reorderVM.getPinned().observe(ReorderTimelinesActivity.this, _pinned -> { + update = true; this.pinned = _pinned; if (this.pinned == null) { this.pinned = new Pinned(); this.pinned.pinnedTimelines = new ArrayList<>(); + update = false; } sortPositionAsc(this.pinned.pinnedTimelines); reorderTabAdapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this, ReorderTimelinesActivity.this); @@ -153,7 +159,6 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra } else if (item.getItemId() == R.id.action_add_timeline) { addInstance(); } - return super.onOptionsItemSelected(item); } @@ -168,11 +173,17 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ReorderTimelinesActivity.this, Helper.dialogStyle()); PopupSearchInstanceBinding popupSearchInstanceBinding = PopupSearchInstanceBinding.inflate(getLayoutInflater()); dialogBuilder.setView(popupSearchInstanceBinding.getRoot()); + TextWatcher textWatcher = autoComplete(popupSearchInstanceBinding); + popupSearchInstanceBinding.searchInstance.addTextChangedListener(textWatcher); + popupSearchInstanceBinding.setAttachmentGroup.setOnCheckedChangeListener((group, checkedId) -> { if (checkedId == R.id.twitter_accounts) { popupSearchInstanceBinding.searchInstance.setHint(R.string.list_of_twitter_accounts); + popupSearchInstanceBinding.searchInstance.removeTextChangedListener(textWatcher); } else { popupSearchInstanceBinding.searchInstance.setHint(R.string.instance); + popupSearchInstanceBinding.searchInstance.removeTextChangedListener(textWatcher); + popupSearchInstanceBinding.searchInstance.addTextChangedListener(textWatcher); } }); popupSearchInstanceBinding.searchInstance.setFilters(new InputFilter[]{new InputFilter.LengthFilter(60)}); @@ -180,16 +191,24 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra String instanceName = popupSearchInstanceBinding.searchInstance.getText().toString().trim().replace("@", ""); new Thread(() -> { String url = null; - if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance) + boolean getCall = true; + RequestBody formBody = new FormBody.Builder() + .build(); + if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance) { url = "https://" + instanceName + "/api/v1/timelines/public?local=true"; - else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance) + } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance) { url = "https://" + instanceName + "/api/v1/videos/"; - else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) { + } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) { url = "https://" + instanceName + "/api/v1/timelines/public"; } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) { url = "https://" + instanceName + "/api/notes/local-timeline"; + getCall = false; } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) { url = "https://" + instanceName + "/api/statuses/public_timeline.json"; + } else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) { + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this); + String nitterHost = sharedpreferences.getString(getString(R.string.SET_NITTER_HOST), getString(R.string.DEFAULT_NITTER_HOST)).toLowerCase(); + url = "https://" + nitterHost + "/" + instanceName.replaceAll("\\s", "") + "/rss"; } OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) @@ -198,9 +217,16 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra .readTimeout(10, TimeUnit.SECONDS).build(); Request request; if (url != null) { - request = new Request.Builder() - .url(url) - .build(); + if (getCall) { + request = new Request.Builder() + .url(url) + .build(); + } else { + request = new Request.Builder() + .url(url) + .post(formBody) + .build(); + } client.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { @@ -236,13 +262,26 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE; pinnedTimeline.position = pinned.pinnedTimelines.size(); pinned.pinnedTimelines.add(pinnedTimeline); - try { - new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned); - changes = true; - reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size()); - } catch (DBException e) { - e.printStackTrace(); + + if (update) { + try { + new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned); + } catch (DBException e) { + e.printStackTrace(); + } + } else { + try { + new Pinned(ReorderTimelinesActivity.this).insertPinned(pinned); + } catch (DBException e) { + e.printStackTrace(); + } } + reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size()); + Bundle b = new Bundle(); + b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true); + Intent intentBD = new Intent(Helper.BROADCAST_DATA); + intentBD.putExtras(b); + LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD); }); } else { runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show()); @@ -267,7 +306,11 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra popupSearchInstanceBinding.searchInstance.setOnItemClickListener((parent, view1, position, id) -> oldSearch = parent.getItemAtPosition(position).toString().trim()); - popupSearchInstanceBinding.searchInstance.addTextChangedListener(new TextWatcher() { + + } + + private TextWatcher autoComplete(PopupSearchInstanceBinding popupSearchInstanceBinding) { + return new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @@ -314,8 +357,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra } } } - }); - + }; } @Override diff --git a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java index 01dfb83ff..69850f5bf 100644 --- a/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java +++ b/app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java @@ -21,12 +21,16 @@ import app.fedilab.android.client.entities.api.Conversation; import app.fedilab.android.client.entities.api.Marker; import app.fedilab.android.client.entities.api.MastodonList; import app.fedilab.android.client.entities.api.Status; +import app.fedilab.android.client.entities.misskey.MisskeyNote; +import app.fedilab.android.client.entities.peertube.PeertubeVideo; import retrofit2.Call; +import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.Field; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.Header; +import retrofit2.http.Headers; import retrofit2.http.POST; import retrofit2.http.PUT; import retrofit2.http.Path; @@ -191,4 +195,33 @@ public interface MastodonTimelinesService { @Field("home[last_read_id]") String home_last_read_id, @Field("notifications[last_read_id]") String notifications_last_read_id ); + + + @Headers({"Accept: application/json"}) + @POST("api/notes") + Call<List<MisskeyNote>> getMisskey(@Body MisskeyNote.MisskeyParams params); + + + //Public timelines for Misskey + @FormUrlEncoded + @POST("api/notes") + Call<List<MisskeyNote>> getMisskey( + @Field("local") boolean local, + @Field("file") boolean file, + @Field("poll") boolean poll, + @Field("remote") boolean remote, + @Field("reply") boolean reply, + @Field("untilId") String max_id, + @Field("since_id") String since_id, + @Field("limit") Integer limit + ); + + @GET("api/v1/videos") + Call<PeertubeVideo> getPeertube( + @Query("start") String start, + @Query("filter") String filter, + @Query("sort") String sort, + @Query("count") int count + ); + } diff --git a/app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java b/app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java new file mode 100644 index 000000000..3b2bcda0e --- /dev/null +++ b/app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java @@ -0,0 +1,152 @@ +package app.fedilab.android.client.entities.misskey; +/* Copyright 2022 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see <http://www.gnu.org/licenses>. */ + + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import app.fedilab.android.client.entities.api.Account; +import app.fedilab.android.client.entities.api.Attachment; +import app.fedilab.android.client.entities.api.Status; + + +@SuppressWarnings("ALL") +public class MisskeyNote implements Serializable { + + @SerializedName("id") + public String id; + @SerializedName("createdAt") + public Date createdAt; + @SerializedName("replyId") + public String replyId; + @SerializedName("cw") + public String cw; + @SerializedName("text") + public String text; + @SerializedName("url") + public String url; + @SerializedName("uri") + public String uri; + @SerializedName("visibility") + public String visibility; + @SerializedName("repliesCount") + public int repliesCount; + @SerializedName("user") + public MisskeyUser user; + @SerializedName("files") + public List<MisskeyFile> files; + @SerializedName("emojis") + public List<MisskeyEmoji> emojis; + + public static Status convert(MisskeyNote misskeyNote) { + Status status = new Status(); + status.id = misskeyNote.id; + status.in_reply_to_id = misskeyNote.replyId; + status.content = misskeyNote.text != null ? misskeyNote.text : ""; + status.text = misskeyNote.text != null ? misskeyNote.text : ""; + status.spoiler_text = misskeyNote.cw; + status.visibility = misskeyNote.visibility; + status.created_at = misskeyNote.createdAt; + status.uri = misskeyNote.uri; + status.url = misskeyNote.url; + + Account account = new Account(); + account.id = misskeyNote.user.id; + account.acct = misskeyNote.user.username; + account.username = misskeyNote.user.username; + account.display_name = misskeyNote.user.name; + account.avatar = misskeyNote.user.avatarUrl; + account.avatar_static = misskeyNote.user.avatarUrl; + status.account = account; + + if (misskeyNote.files != null && misskeyNote.files.size() > 0) { + List<Attachment> attachmentList = new ArrayList<>(); + for (MisskeyFile misskeyFile : misskeyNote.files) { + Attachment attachment = new Attachment(); + attachment.type = misskeyFile.type; + attachment.description = misskeyFile.comment; + attachment.url = misskeyFile.url; + attachment.preview_url = misskeyFile.thumbnailUrl; + if (misskeyFile.isSensitive) { + status.sensitive = true; + } + attachmentList.add(attachment); + } + status.media_attachments = attachmentList; + } + + return status; + } + + public static class MisskeyUser implements Serializable { + @SerializedName("id") + public String id; + @SerializedName("name") + public String name; + @SerializedName("username") + public String username; + @SerializedName("avatarUrl") + public String avatarUrl; + @SerializedName("emojis") + public List<MisskeyEmoji> emojis; + } + + public static class MisskeyFile implements Serializable { + @SerializedName("id") + public String id; + @SerializedName("comment") + public String comment; + @SerializedName("isSensitive") + public boolean isSensitive; + @SerializedName("thumbnailUrl") + public String thumbnailUrl; + @SerializedName("url") + public String url; + @SerializedName("type") + public String type; + } + + public static class MisskeyEmoji implements Serializable { + @SerializedName("name") + public String name; + @SerializedName("comment") + public String url; + } + + public static class MisskeyParams implements Serializable { + @SerializedName("local") + public boolean local = true; + @SerializedName("file") + public boolean file = false; + @SerializedName("poll") + public boolean poll = false; + @SerializedName("remote") + public boolean remote = false; + @SerializedName("reply") + public boolean reply = false; + @SerializedName("max_id") + public String max_id; + @SerializedName("since_id") + public String since_id; + @SerializedName("limit") + public int limit; + } + +} diff --git a/app/src/main/java/app/fedilab/android/client/entities/peertube/PeertubeVideo.java b/app/src/main/java/app/fedilab/android/client/entities/peertube/PeertubeVideo.java new file mode 100644 index 000000000..90f323813 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/client/entities/peertube/PeertubeVideo.java @@ -0,0 +1,187 @@ +package app.fedilab.android.client.entities.peertube; +/* Copyright 2022 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see <http://www.gnu.org/licenses>. */ + + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import app.fedilab.android.client.entities.api.Account; +import app.fedilab.android.client.entities.api.Attachment; +import app.fedilab.android.client.entities.api.Status; + +@SuppressWarnings("ALL") +public class PeertubeVideo implements Serializable { + + @SerializedName("total") + public int total; + @SerializedName("data") + public List<Video> data; + + public static Status convert(Video peertubeVideo) { + Status status = new Status(); + status.id = peertubeVideo.id; + status.content = peertubeVideo.description != null ? peertubeVideo.description : ""; + status.text = peertubeVideo.description; + status.visibility = "public"; + status.created_at = peertubeVideo.publishedAt; + status.uri = peertubeVideo.uuid; + status.sensitive = peertubeVideo.nsfw; + status.url = "https://" + peertubeVideo.account.host + "/videos/watch/" + peertubeVideo.uuid; + Account account = new Account(); + account.id = peertubeVideo.channel.id; + account.acct = peertubeVideo.channel.name; + account.username = peertubeVideo.channel.name; + account.display_name = peertubeVideo.channel.displayName; + if (peertubeVideo.channel.avatar != null) { + account.avatar = "https://" + peertubeVideo.account.host + peertubeVideo.channel.avatar.path; + account.avatar_static = "https://" + peertubeVideo.account.host + peertubeVideo.channel.avatar.path; + } + status.account = account; + List<Attachment> attachmentList = new ArrayList<>(); + Attachment attachment = new Attachment(); + attachment.type = "video/mp4"; + attachment.url = "https://" + peertubeVideo.account.host + peertubeVideo.embedPath; + attachment.preview_url = "https://" + peertubeVideo.account.host + peertubeVideo.thumbnailPath; + attachmentList.add(attachment); + status.media_attachments = attachmentList; + return status; + } + + public static class Video implements Serializable { + @SerializedName("account") + public PeertubeAccount account; + @SerializedName("category") + public Item category; + @SerializedName("channel") + public Channel channel; + @SerializedName("createdAt") + public Date createdAt; + @SerializedName("description") + public String description; + @SerializedName("duration") + public int duration; + @SerializedName("embedPath") + public String embedPath; + @SerializedName("id") + public String id; + @SerializedName("isLive") + public boolean isLive = false; + @SerializedName("url") + public String url; + @SerializedName("isLocal") + public boolean isLocal; + @SerializedName("language") + public ItemStr language; + @SerializedName("licence") + public Item licence; + @SerializedName("likes") + public int likes; + @SerializedName("name") + public String name; + @SerializedName("nsfw") + public boolean nsfw; + @SerializedName("originallyPublishedAt") + public Date originallyPublishedAt; + @SerializedName("previewPath") + public String previewPath; + @SerializedName("privacy") + public Item privacy; + @SerializedName("publishedAt") + public Date publishedAt; + @SerializedName("thumbnailPath") + public String thumbnailPath; + @SerializedName("updatedAt") + public Date updatedAt; + @SerializedName("uuid") + public String uuid; + + @SerializedName("views") + public int views; + } + + public static class PeertubeAccount implements Serializable { + @SerializedName("avatar") + public Avatar avatar; + @SerializedName("createdAt") + public Date createdAt; + @SerializedName("description") + public String description; + @SerializedName("displayName") + public String displayName; + @SerializedName("followersCount") + public int followersCount; + @SerializedName("followingCount") + public int followingCount; + @SerializedName("host") + public String host; + @SerializedName("hostRedundancyAllowed") + public boolean hostRedundancyAllowed; + @SerializedName("id") + public String id; + @SerializedName("name") + public String name; + @SerializedName("username") + public String username; + @SerializedName("updatedAt") + public Date updatedAt; + @SerializedName("url") + public String url; + @SerializedName("userId") + public String userId; + } + + public static class Item implements Serializable { + @SerializedName("id") + public int id; + @SerializedName("label") + public String label; + } + + public static class ItemStr implements Serializable { + @SerializedName("id") + public String id; + @SerializedName("label") + public String label; + } + + public static class Channel implements Serializable { + @SerializedName("avatar") + public Avatar avatar; + @SerializedName("displayName") + public String displayName; + @SerializedName("host") + public String host; + @SerializedName("id") + public String id; + @SerializedName("name") + public String name; + @SerializedName("url") + public String url; + } + + public static class Avatar implements Serializable { + @SerializedName("createdAt") + public Date createdAt; + @SerializedName("path") + public String path; + @SerializedName("updatedAt") + public Date updatedAt; + } +} diff --git a/app/src/main/java/app/fedilab/android/helper/MastodonHelper.java b/app/src/main/java/app/fedilab/android/helper/MastodonHelper.java index 283473488..3c966a5f6 100644 --- a/app/src/main/java/app/fedilab/android/helper/MastodonHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/MastodonHelper.java @@ -213,19 +213,27 @@ public class MastodonHelper { return; } String targetedUrl = disableGif ? (type == MediaAccountType.AVATAR ? account.avatar_static : account.header_static) : (type == MediaAccountType.AVATAR ? account.avatar : account.header); - if (disableGif || (!targetedUrl.endsWith(".gif"))) { - Glide.with(view.getContext()) - .asDrawable() - .load(targetedUrl) - .thumbnail(0.1f) - .placeholder(placeholder) - .into(view); + if (targetedUrl != null) { + if (disableGif || (!targetedUrl.endsWith(".gif"))) { + Glide.with(view.getContext()) + .asDrawable() + .load(targetedUrl) + .thumbnail(0.1f) + .placeholder(placeholder) + .into(view); + } else { + Glide.with(view.getContext()) + .asGif() + .load(targetedUrl) + .thumbnail(0.1f) + .placeholder(placeholder) + .into(view); + } } else { Glide.with(view.getContext()) - .asGif() - .load(targetedUrl) + .asDrawable() + .load(placeholder) .thumbnail(0.1f) - .placeholder(placeholder) .into(view); } } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index 7eeeb17ae..618f5e205 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -48,7 +48,9 @@ import app.fedilab.android.client.entities.api.Marker; import app.fedilab.android.client.entities.api.Pagination; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Statuses; +import app.fedilab.android.client.entities.app.PinnedTimeline; import app.fedilab.android.client.entities.app.QuickLoad; +import app.fedilab.android.client.entities.app.RemoteInstance; import app.fedilab.android.client.entities.app.TagTimeline; import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.FragmentPaginationBinding; @@ -136,6 +138,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. private Account accountTimeline; private boolean exclude_replies, exclude_reblogs, show_pinned, media_only, minified; private String viewModelKey, remoteInstance; + private PinnedTimeline pinnedTimeline; private String ident; private String instance, user_id; private ArrayList<String> idOfAddedStatuses; @@ -199,7 +202,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. list_id = getArguments().getString(Helper.ARG_LIST_ID, null); search = getArguments().getString(Helper.ARG_SEARCH_KEYWORD, null); searchCache = getArguments().getString(Helper.ARG_SEARCH_KEYWORD_CACHE, null); - remoteInstance = getArguments().getString(Helper.ARG_REMOTE_INSTANCE, null); + pinnedTimeline = (PinnedTimeline) getArguments().getSerializable(Helper.ARG_REMOTE_INSTANCE); + if (pinnedTimeline != null && pinnedTimeline.remoteInstance != null) { + remoteInstance = pinnedTimeline.remoteInstance.host; + } + tagTimeline = (TagTimeline) getArguments().getSerializable(Helper.ARG_TAG_TIMELINE); accountTimeline = (Account) getArguments().getSerializable(Helper.ARG_ACCOUNT); exclude_replies = !getArguments().getBoolean(Helper.ARG_SHOW_REPLIES, true); @@ -632,24 +639,75 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. }); } } else if (timelineType == Timeline.TimeLineEnum.REMOTE) { //REMOTE TIMELINE - if (direction == null) { - timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); - } else if (direction == DIRECTION.BOTTOM) { - timelinesVM.getPublic(null, remoteInstance, true, false, false, max_id, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); - } else if (direction == DIRECTION.TOP) { - timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); - } else if (direction == DIRECTION.REFRESH) { - timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) - .observe(getViewLifecycleOwner(), statusesRefresh -> { - if (statusAdapter != null) { - dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); - } else { - initializeStatusesCommonView(statusesRefresh); - } - }); + + //NITTER TIMELINES + if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.NITTER) { + + } //GNU TIMELINES + else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.GNU) { + + }//MISSKEY TIMELINES + else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.MISSKEY) { + if (direction == null) { + timelinesVM.getMisskey(remoteInstance, null, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); + } else if (direction == DIRECTION.BOTTOM) { + timelinesVM.getMisskey(remoteInstance, max_id, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + } else if (direction == DIRECTION.TOP) { + timelinesVM.getMisskey(remoteInstance, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); + } else if (direction == DIRECTION.REFRESH) { + timelinesVM.getMisskey(remoteInstance, null, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesRefresh -> { + if (statusAdapter != null) { + dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); + } else { + initializeStatusesCommonView(statusesRefresh); + } + }); + } + } //PEERTUBE TIMELINES + else if (pinnedTimeline != null && pinnedTimeline.remoteInstance.type == RemoteInstance.InstanceType.PEERTUBE) { + if (direction == null) { + + timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), this::initializeStatusesCommonView); + } else if (direction == DIRECTION.BOTTOM) { + timelinesVM.getPeertube(remoteInstance, max_id, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); + } else if (direction == DIRECTION.TOP) { + flagLoading = false; + } else if (direction == DIRECTION.REFRESH) { + timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity())) + .observe(getViewLifecycleOwner(), statusesRefresh -> { + if (statusAdapter != null) { + dealWithPagination(statusesRefresh |