Loading app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java +59 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; } Loading @@ -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); Loading Loading @@ -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); } Loading @@ -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)}); Loading @@ -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) Loading @@ -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) { Loading Loading @@ -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()); Loading @@ -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) { } Loading Loading @@ -314,8 +357,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra } } } }); }; } @Override Loading app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java +33 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 ); } app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java 0 → 100644 +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; } } app/src/main/java/app/fedilab/android/client/entities/peertube/PeertubeVideo.java 0 → 100644 +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; } } app/src/main/java/app/fedilab/android/helper/MastodonHelper.java +18 −10 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -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 Loading
app/src/main/java/app/fedilab/android/activities/ReorderTimelinesActivity.java +59 −17 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; } Loading @@ -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); Loading Loading @@ -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); } Loading @@ -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)}); Loading @@ -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) Loading @@ -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) { Loading Loading @@ -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()); Loading @@ -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) { } Loading Loading @@ -314,8 +357,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra } } } }); }; } @Override Loading
app/src/main/java/app/fedilab/android/client/endpoints/MastodonTimelinesService.java +33 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 ); }
app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java 0 → 100644 +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; } }
app/src/main/java/app/fedilab/android/client/entities/peertube/PeertubeVideo.java 0 → 100644 +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; } }
app/src/main/java/app/fedilab/android/helper/MastodonHelper.java +18 −10 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -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