summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas <tschneider.ac@gmail.com>2022-07-19 15:49:47 +0200
committerThomas <tschneider.ac@gmail.com>2022-07-19 15:49:47 +0200
commit8a0460536cdd4e3489bf64a858ca3bab6efb2201 (patch)
tree7b20a29f697f53da92ecaea3bf1e2aedbc49c48b
parentcf1a1b3e53040e3ffef67052e85b35b3fda95f70 (diff)
Add emoji reactions for Pleroma
-rw-r--r--app/src/main/java/app/fedilab/android/BaseMainActivity.java2
-rw-r--r--app/src/main/java/app/fedilab/android/client/endpoints/PleromaAPI.java40
-rw-r--r--app/src/main/java/app/fedilab/android/client/entities/api/Pleroma.java25
-rw-r--r--app/src/main/java/app/fedilab/android/client/entities/api/Reaction.java4
-rw-r--r--app/src/main/java/app/fedilab/android/client/entities/api/Status.java2
-rw-r--r--app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java17
-rw-r--r--app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java2
-rw-r--r--app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java122
-rw-r--r--app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java8
-rw-r--r--app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java4
-rw-r--r--app/src/main/java/app/fedilab/android/viewmodel/pleroma/ActionsVM.java101
-rw-r--r--app/src/main/res/layout/drawer_announcement.xml61
-rw-r--r--app/src/main/res/layout/drawer_status.xml6
-rw-r--r--app/src/main/res/layout/layout_reactions.xml60
14 files changed, 378 insertions, 76 deletions
diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java
index 231becca3..6a490eb22 100644
--- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java
+++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java
@@ -855,7 +855,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
binding.toolbarSearch.setOnSearchClickListener(v -> binding.tabLayout.setVisibility(View.VISIBLE));
//For receiving data from other activities
LocalBroadcastManager.getInstance(BaseMainActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
- if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance)) {
+ if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance) || emojis.get(BaseMainActivity.currentInstance) == null) {
new Thread(() -> {
try {
emojis.put(currentInstance, new EmojiInstance(BaseMainActivity.this).getEmojiList(BaseMainActivity.currentInstance));
diff --git a/app/src/main/java/app/fedilab/android/client/endpoints/PleromaAPI.java b/app/src/main/java/app/fedilab/android/client/endpoints/PleromaAPI.java
new file mode 100644
index 000000000..d1309bb28
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/client/endpoints/PleromaAPI.java
@@ -0,0 +1,40 @@
+package app.fedilab.android.client.endpoints;
+/* 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 retrofit2.Call;
+import retrofit2.http.DELETE;
+import retrofit2.http.Header;
+import retrofit2.http.PUT;
+import retrofit2.http.Path;
+
+public interface PleromaAPI {
+
+
+ @PUT("pleroma/statuses/{id}/reactions/{name}")
+ Call<Void> addReaction(
+ @Header("Authorization") String app_token,
+ @Path("id") String id,
+ @Path("name") String name
+ );
+
+ @DELETE("pleroma/statuses/{id}/reactions/{name}")
+ Call<Void> removeReaction(
+ @Header("Authorization") String app_token,
+ @Path("id") String id,
+ @Path("name") String name
+ );
+
+}
diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Pleroma.java b/app/src/main/java/app/fedilab/android/client/entities/api/Pleroma.java
new file mode 100644
index 000000000..bee1912bf
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/client/entities/api/Pleroma.java
@@ -0,0 +1,25 @@
+package app.fedilab.android.client.entities.api;
+/* 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.List;
+
+public class Pleroma implements Serializable {
+ @SerializedName("emoji_reactions")
+ public List<Reaction> emoji_reactions;
+}
diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Reaction.java b/app/src/main/java/app/fedilab/android/client/entities/api/Reaction.java
index 396355247..028f93ef1 100644
--- a/app/src/main/java/app/fedilab/android/client/entities/api/Reaction.java
+++ b/app/src/main/java/app/fedilab/android/client/entities/api/Reaction.java
@@ -16,7 +16,9 @@ package app.fedilab.android.client.entities.api;
import com.google.gson.annotations.SerializedName;
-public class Reaction {
+import java.io.Serializable;
+
+public class Reaction implements Serializable {
@SerializedName("name")
public String name;
@SerializedName("count")
diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java
index b75125e18..144eea3e7 100644
--- a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java
+++ b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java
@@ -89,6 +89,8 @@ public class Status implements Serializable, Cloneable {
public Card card;
@SerializedName("poll")
public Poll poll;
+ @SerializedName("pleroma")
+ public Pleroma pleroma;
public Attachment art_attachment;
diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java
index 6a6d9fad9..abf0c05f5 100644
--- a/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java
+++ b/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java
@@ -76,17 +76,18 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
return new AnnouncementHolder(itemBinding);
}
+
@Override
public void onBindViewHolder(@NonNull AnnouncementHolder holder, int position) {
Announcement announcement = announcements.get(position);
if (announcement.reactions != null && announcement.reactions.size() > 0) {
ReactionAdapter reactionAdapter = new ReactionAdapter(announcement.id, announcement.reactions);
- holder.binding.reactionsView.setAdapter(reactionAdapter);
+ holder.binding.layoutReactions.reactionsView.setAdapter(reactionAdapter);
LinearLayoutManager layoutManager
= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
- holder.binding.reactionsView.setLayoutManager(layoutManager);
+ holder.binding.layoutReactions.reactionsView.setLayoutManager(layoutManager);
} else {
- holder.binding.reactionsView.setAdapter(null);
+ holder.binding.layoutReactions.reactionsView.setAdapter(null);
}
holder.binding.content.setText(
announcement.getSpanContent(context,
@@ -108,11 +109,11 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
} else {
holder.binding.dates.setVisibility(View.GONE);
}
- holder.binding.statusEmoji.setOnClickListener(v -> {
+ holder.binding.layoutReactions.statusEmoji.setOnClickListener(v -> {
EmojiManager.install(new EmojiOneProvider());
- final EmojiPopup emojiPopup = EmojiPopup.Builder.fromRootView(holder.binding.statusEmoji).setOnEmojiPopupDismissListener(() -> {
+ final EmojiPopup emojiPopup = EmojiPopup.Builder.fromRootView(holder.binding.layoutReactions.statusEmoji).setOnEmojiPopupDismissListener(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(holder.binding.statusEmoji.getWindowToken(), 0);
+ imm.hideSoftInputFromWindow(holder.binding.layoutReactions.statusEmoji.getWindowToken(), 0);
}).setOnEmojiClickListener((emoji, imageView) -> {
String emojiStr = imageView.getUnicode();
boolean alreadyAdded = false;
@@ -142,10 +143,10 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
announcementsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcement.id, emojiStr);
}
})
- .build(holder.binding.fakeEdittext);
+ .build(holder.binding.layoutReactions.fakeEdittext);
emojiPopup.toggle();
});
- holder.binding.statusAddCustomEmoji.setOnClickListener(v -> {
+ holder.binding.layoutReactions.statusAddCustomEmoji.setOnClickListener(v -> {
final AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
int paddingPixel = 15;
float density = context.getResources().getDisplayMetrics().density;
diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java
index 9f849b1f6..7a3819ca3 100644
--- a/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java
+++ b/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java
@@ -42,7 +42,7 @@ public class EmojiAdapter extends BaseAdapter {
}
public int getCount() {
- return emojiList.size();
+ return emojiList == null ? 0 : emojiList.size();
}
public Emoji getItem(int position) {
diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java
index d0f2cc4cc..628f39d8e 100644
--- a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java
+++ b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java
@@ -15,6 +15,8 @@ package app.fedilab.android.ui.drawer;
* see <http://www.gnu.org/licenses>. */
+import static android.content.Context.INPUT_METHOD_SERVICE;
+import static app.fedilab.android.BaseMainActivity.emojis;
import static app.fedilab.android.BaseMainActivity.regex_home;
import static app.fedilab.android.BaseMainActivity.regex_local;
import static app.fedilab.android.BaseMainActivity.regex_public;
@@ -46,7 +48,9 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
+import android.widget.GridView;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
@@ -66,6 +70,7 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
@@ -78,6 +83,9 @@ import com.github.stom79.mytransl.client.HttpsConnectionException;
import com.github.stom79.mytransl.client.Results;
import com.github.stom79.mytransl.translate.Params;
import com.github.stom79.mytransl.translate.Translate;
+import com.vanniktech.emoji.EmojiManager;
+import com.vanniktech.emoji.EmojiPopup;
+import com.vanniktech.emoji.one.EmojiOneProvider;
import com.varunest.sparkbutton.SparkButton;
import java.lang.ref.WeakReference;
@@ -93,6 +101,7 @@ import app.fedilab.android.R;
import app.fedilab.android.activities.ComposeActivity;
import app.fedilab.android.activities.ContextActivity;
import app.fedilab.android.activities.CustomSharingActivity;
+import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.activities.MediaActivity;
import app.fedilab.android.activities.ProfileActivity;
import app.fedilab.android.activities.ReportActivity;
@@ -100,7 +109,9 @@ import app.fedilab.android.activities.StatusInfoActivity;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Notification;
import app.fedilab.android.client.entities.api.Poll;
+import app.fedilab.android.client.entities.api.Reaction;
import app.fedilab.android.client.entities.api.Status;
+import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.client.entities.app.Timeline;
@@ -124,6 +135,7 @@ import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.viewmodel.mastodon.SearchVM;
import app.fedilab.android.viewmodel.mastodon.StatusesVM;
+import app.fedilab.android.viewmodel.pleroma.ActionsVM;
import es.dmoral.toasty.Toasty;
import jp.wasabeef.glide.transformations.BlurTransformation;
@@ -313,6 +325,116 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
boolean fullAttachement = sharedpreferences.getBoolean(context.getString(R.string.SET_FULL_PREVIEW), false);
boolean displayBookmark = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_BOOKMARK), false);
+ if (MainActivity.currentAccount != null && MainActivity.currentAccount.api == Account.API.PLEROMA) {
+ holder.binding.layoutReactions.getRoot().setVisibility(View.VISIBLE);
+ if (status.pleroma != null && status.pleroma.emoji_reactions != null && status.pleroma.emoji_reactions.size() > 0) {
+ ReactionAdapter reactionAdapter = new ReactionAdapter(status.id, status.pleroma.emoji_reactions);
+ holder.binding.layoutReactions.reactionsView.setAdapter(reactionAdapter);
+ LinearLayoutManager layoutManager
+ = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
+ holder.binding.layoutReactions.reactionsView.setLayoutManager(layoutManager);
+ } else {
+ holder.binding.layoutReactions.reactionsView.setAdapter(null);
+ }
+ holder.binding.layoutReactions.statusEmoji.setOnClickListener(v -> {
+ EmojiManager.install(new EmojiOneProvider());
+ final EmojiPopup emojiPopup = EmojiPopup.Builder.fromRootView(holder.binding.layoutReactions.statusEmoji).setOnEmojiPopupDismissListener(() -> {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(holder.binding.layoutReactions.statusEmoji.getWindowToken(), 0);
+ }).setOnEmojiClickListener((emoji, imageView) -> {
+ String emojiStr = imageView.getUnicode();
+ boolean alreadyAdded = false;
+ for (Reaction reaction : status.pleroma.emoji_reactions) {
+ if (reaction.name.compareTo(emojiStr) == 0) {
+ alreadyAdded = true;
+ reaction.count = (reaction.count - 1);
+ if (reaction.count == 0) {
+ status.pleroma.emoji_reactions.remove(reaction);
+ }
+ adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
+ break;
+ }
+ }
+ if (!alreadyAdded) {
+ Reaction reaction = new Reaction();
+ reaction.me = true;
+ reaction.count = 1;
+ reaction.name = emojiStr;
+ status.pleroma.emoji_reactions.add(0, reaction);
+ adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
+ }
+ ActionsVM actionVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
+ if (alreadyAdded) {
+ actionVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
+ } else {
+ actionVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
+ }
+ })
+ .build(holder.binding.layoutReactions.fakeEdittext);
+ emojiPopup.toggle();
+ });
+ holder.binding.layoutReactions.statusAddCustomEmoji.setOnClickListener(v -> {
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
+ int paddingPixel = 15;
+ float density = context.getResources().getDisplayMetrics().density;
+ int paddingDp = (int) (paddingPixel * density);
+ builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
+ builder.setTitle(R.string.insert_emoji);
+ AlertDialog alertDialogEmoji = null;
+ if (emojis != null && emojis.size() > 0 && emojis.get(BaseMainActivity.currentInstance) != null) {
+ GridView gridView = new GridView(context);
+ gridView.setAdapter(new EmojiAdapter(emojis.get(BaseMainActivity.currentInstance)));
+ gridView.setNumColumns(5);
+ AlertDialog finalAlertDialogEmoji = alertDialogEmoji;
+ gridView.setOnItemClickListener((parent, view, index, id) -> {
+ String emojiStr = emojis.get(BaseMainActivity.currentInstance).get(index).shortcode;
+ String url = emojis.get(BaseMainActivity.currentInstance).get(index).url;
+ String static_url = emojis.get(BaseMainActivity.currentInstance).get(index).static_url;
+ boolean alreadyAdded = false;
+ for (Reaction reaction : status.pleroma.emoji_reactions) {
+ if (reaction.name.compareTo(emojiStr) == 0) {
+ alreadyAdded = true;
+ reaction.count = (reaction.count - 1);
+ if (reaction.count == 0) {
+ status.pleroma.emoji_reactions.remove(reaction);
+ }
+ adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
+ break;
+ }
+ }
+ if (!alreadyAdded) {
+ Reaction reaction = new Reaction();
+ reaction.me = true;
+ reaction.count = 1;
+ reaction.name = emojiStr;
+ reaction.url = url;
+ reaction.static_url = static_url;
+ status.pleroma.emoji_reactions.add(0, reaction);
+ adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
+ }
+ ActionsVM actionsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
+ if (alreadyAdded) {
+ actionsVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
+ } else {
+ actionsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
+ }
+ if (finalAlertDialogEmoji != null) {
+ finalAlertDialogEmoji.dismiss();
+ }
+ });
+ gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
+ builder.setView(gridView);
+ } else {
+ TextView textView = new TextView(context);
+ textView.setText(context.getString(R.string.no_emoji));
+ textView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
+ builder.setView(textView);
+ }
+ alertDialogEmoji = builder.show();
+ });
+ }
+
int truncate_toots_size = sharedpreferences.getInt(context.getString(R.string.SET_TRUNCATE_TOOTS_SIZE), 0);
// boolean display_video_preview = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_VIDEO_PREVIEWS), true);
// boolean isModerator = sharedpreferences.getBoolean(Helper.PREF_IS_MODERATOR, false);
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 c540f269f..53fd1aaef 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
@@ -174,9 +174,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
}
public void scrollToTop() {
- binding.swipeContainer.setRefreshing(true);
- flagLoading = false;
- route(DIRECTION.SCROLL_TOP, true);
+ if (binding != null) {
+ binding.swipeContainer.setRefreshing(true);
+ flagLoading = false;
+ route(DIRECTION.SCROLL_TOP, true);
+ }
}
public View onCreateView(@NonNull LayoutInflater inflater,
diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java
index 743cd32a8..06eccbfd4 100644
--- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java
+++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java
@@ -23,9 +23,6 @@ import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -54,7 +51,6 @@ public class AnnouncementsVM extends AndroidViewModel {
}
private MastodonAnnouncementsService init(@NonNull String instance) {
- Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + instance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
diff --git a/app/src/main/java/app/fedilab/android/viewmodel/pleroma/ActionsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/pleroma/ActionsVM.java
new file mode 100644
index 000000000..c57b52ea7
--- /dev/null
+++ b/app/src/main/java/app/fedilab/android/viewmodel/pleroma/ActionsVM.java
@@ -0,0 +1,101 @@
+package app.fedilab.android.viewmodel.pleroma;
+/* 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 android.app.Application;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.MutableLiveData;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import app.fedilab.android.client.endpoints.PleromaAPI;
+import app.fedilab.android.client.entities.api.Announcement;
+import app.fedilab.android.helper.Helper;
+import okhttp3.OkHttpClient;
+import retrofit2.Call;
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+public class ActionsVM extends AndroidViewModel {
+
+ final OkHttpClient okHttpClient = new OkHttpClient.Builder()
+ .readTimeout(60, TimeUnit.SECONDS)
+ .connectTimeout(60, TimeUnit.SECONDS)
+ .callTimeout(60, TimeUnit.SECONDS)
+ .proxy(Helper.getProxy(getApplication().getApplicationContext()))
+ .build();
+ private MutableLiveData<Announcement> announcementMutableLiveData;
+ private MutableLiveData<List<Announcement>> announcementListMutableLiveData;
+
+ public ActionsVM(@NonNull Application application) {
+ super(application);
+ }
+
+ private PleromaAPI init(@NonNull String instance) {
+ Retrofit retrofit = new Retrofit.Builder()
+ .baseUrl("https://" + instance + "/api/v1/")
+ .addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
+ .client(okHttpClient)
+ .build();
+ return retrofit.create(PleromaAPI.class);
+ }
+
+ /**
+ * React to an announcement with an emoji.
+ *
+ * @param instance Instance domain of the active account
+ * @param token Access token of the active account
+ * @param id Local ID of an announcement
+ * @param name Unicode emoji, or shortcode of custom emoji
+ */
+ public void addReaction(@NonNull String instance, String token, @NonNull String id, @NonNull String name) {
+ PleromaAPI pleromaAPI = init(instance);
+ new Thread(() -> {
+ Call<Void> addReactionCall = pleromaAPI.addReaction(token, id, name);
+ if (addReactionCall != null) {
+ try {
+ addReactionCall.execute();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * Undo a react emoji to an announcement.
+ *
+ * @param instance Instance domain of the active account
+ * @param token Access token of the active account
+ * @param id Local ID of an announcement
+ * @param name Unicode emoji, or shortcode of custom emoji
+ */
+ public void removeReaction(@NonNull String instance, String token, @NonNull String id, @NonNull String name) {
+ PleromaAPI pleromaAPI = init(instance);
+ new Thread(() -> {
+ Call<Void> removeReactionCall = pleromaAPI.removeReaction(token, id, name);
+ if (removeReactionCall != null) {
+ try {
+ removeReactionCall.execute();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+}
diff --git a/app/src/main/res/layout/drawer_announcement.xml b/app/src/main/res/layout/drawer_announcement.xml
index fdb2d27e0..c0c408076 100644
--- a/app/src/main/res/layout/drawer_announcement.xml
+++ b/app/src/main/res/layout/drawer_announcement.xml
@@ -59,64 +59,9 @@
tools:maxLines="10"
tools:text="@tools:sample/lorem/random" />
- <androidx.appcompat.widget.LinearLayoutCompat
- android:id="@+id/status_reactions"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="10dp"
- android:paddingBottom="10dp"
- android:orientation="horizontal"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/content">
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/reactions_view"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1" />
-
- <androidx.appcompat.widget.AppCompatImageView
- android:id="@+id/status_add_custom_emoji"
- android:layout_width="30dp"
- android:layout_height="30dp"
- android:layout_marginStart="10dp"
- android:layout_marginEnd="5dp"
- android:contentDescription="@string/add_reaction"
- android:src="@drawable/ic_baseline_emoji_emotions_24"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:tint="?attr/iconColor" />
-
- <androidx.appcompat.widget.AppCompatImageView
- android:id="@+id/status_emoji"
- android:layout_width="30dp"
- android:layout_height="30dp"
- android:layout_marginStart="10dp"
- android:layout_marginEnd="5dp"
- android:contentDescription="@string/add_reaction"
- android:src="@drawable/ic_baseline_add_reaction_24"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:tint="?attr/iconColor" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone">
-
- <app.fedilab.android.helper.FedilabAutoCompleteTextView
- android:id="@+id/fake_edittext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:importantForAutofill="noExcludeDescendants"
- android:inputType="text" />
- </LinearLayout>
- </androidx.appcompat.widget.LinearLayoutCompat>
+ <include
+ android:id="@+id/layout_reactions"
+ layout="@layout/layout_reactions" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView> \ No newline at end of file
diff --git a/app/src/main/res/layout/drawer_status.xml b/app/src/main/res/layout/drawer_status.xml
index c73a3c890..b6caece7a 100644
--- a/app/src/main/res/layout/drawer_status.xml
+++ b/app/src/main/res/layout/drawer_status.xml
@@ -601,6 +601,12 @@
</androidx.appcompat.widget.LinearLayoutCompat>
+ <include
+ android:id="@+id/layout_reactions"
+ layout="@layout/layout_reactions"
+ android:visibility="gone"
+ tools:visibility="visible" />
+
</androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView>
diff --git a/app/src/main/res/layout/layout_reactions.xml b/app/src/main/res/layout/layout_reactions.xml
new file mode 100644
index 000000000..46e4b1a0a
--- /dev/null
+++ b/app/src/main/res/layout/layout_reactions.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/status_reactions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal"
+ android:paddingBottom="10dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/content">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/reactions_view"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/status_add_custom_emoji"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="5dp"
+ android:contentDescription="@string/add_reaction"
+ android:src="@drawable/ic_baseline_emoji_emotions_24"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:tint="?attr/iconColor" />
+
+ <androidx.appcompat.widget.AppCompatImageView
+ android:id="@+id/status_emoji"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="5dp"
+ android:contentDescription="@string/add_reaction"
+ android:src="@drawable/ic_baseline_add_reaction_24"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:tint="?attr/iconColor" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+
+ <app.fedilab.android.helper.FedilabAutoCompleteTextView
+ android:id="@+id/fake_edittext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:importantForAutofill="noExcludeDescendants"
+ android:inputType="text" />
+ </LinearLayout>
+</androidx.appcompat.w