Commit 79d03703 authored by Thomas's avatar Thomas
Browse files

Follow Peertube & Misskey instances

parent e32101a6
Loading
Loading
Loading
Loading
+59 −17
Original line number Diff line number Diff line
@@ -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) {
                    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);

                                    if (update) {
                                        try {
                                            new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
                                        changes = true;
                                        reorderTabAdapter.notifyItemInserted(pinned.pinnedTimelines.size());
                                        } 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
+33 −0
Original line number Diff line number Diff line
@@ -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
    );

}
+152 −0
Original line number Diff line number Diff line
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;
    }

}
+187 −0
Original line number Diff line number Diff line
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;
    }
}
+18 −10
Original line number Diff line number Diff line
@@ -213,6 +213,7 @@ public class MastodonHelper {
                return;
            }
            String targetedUrl = disableGif ? (type == MediaAccountType.AVATAR ? account.avatar_static : account.header_static) : (type == MediaAccountType.AVATAR ? account.avatar : account.header);
            if (targetedUrl != null) {
                if (disableGif || (!targetedUrl.endsWith(".gif"))) {
                    Glide.with(view.getContext())
                            .asDrawable()
@@ -228,6 +229,13 @@ public class MastodonHelper {
                            .placeholder(placeholder)
                            .into(view);
                }
            } else {
                Glide.with(view.getContext())
                        .asDrawable()
                        .load(placeholder)
                        .thumbnail(0.1f)
                        .into(view);
            }
        }
    }

Loading