Add like button on Pixelfed timelines

This commit is contained in:
Thomas 2025-02-27 15:06:51 +01:00
parent f94251776b
commit 310d6bf4aa
4 changed files with 168 additions and 5 deletions

View file

@ -34,7 +34,6 @@ import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_ID;
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_INSTANCE;
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_SOFTWARE;
import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_TOKEN;
import static app.fedilab.android.mastodon.helper.Helper.TAG;
import static app.fedilab.android.mastodon.helper.Helper.getCurrentAccount;
import android.annotation.SuppressLint;
@ -59,7 +58,6 @@ import android.os.Looper;
import android.text.Html;
import android.text.SpannableString;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@ -305,6 +303,47 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
Status statusToDeal,
Status statusReturned,
boolean remote) {
manageSubAction(context, holder, typeOfAction, statusToDeal, statusReturned, remote);
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
}
/***
* Methode that will deal with results of actions (bookmark, favourite, boost)
* @param context Context
* @param adapter RecyclerView.Adapter<RecyclerView.ViewHolder>
* @param holder StatusViewHolder used by the reycler
* @param typeOfAction CrossActionHelper.TypeOfCrossAction
* @param statusToDeal Status that received the action
* @param statusReturned Status returned by the API
* @param remote boolean - it's a remote message
*/
private static void manageAction(Context context,
SliderAdapter adapter,
StatusViewHolder holder,
CrossActionHelper.TypeOfCrossAction typeOfAction,
Status statusToDeal,
Status statusReturned,
boolean remote) {
manageSubAction(context, holder, typeOfAction, statusToDeal, statusReturned, remote);
adapter.notifyDataSetChanged();
}
/***
* Methode that will deal with results of actions (bookmark, favourite, boost)
* @param context Context
* @param holder StatusViewHolder used by the reycler
* @param typeOfAction CrossActionHelper.TypeOfCrossAction
* @param statusToDeal Status that received the action
* @param statusReturned Status returned by the API
* @param remote boolean - it's a remote message
*/
private static void manageSubAction(Context context,
StatusViewHolder holder,
CrossActionHelper.TypeOfCrossAction typeOfAction,
Status statusToDeal,
Status statusReturned,
boolean remote) {
if (statusReturned == null) {
switch (typeOfAction) {
case BOOKMARK_ACTION -> statusToDeal.bookmarked = true;
@ -378,9 +417,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
}).start();
}
sendAction(context, Helper.ARG_STATUS_ACTION, statusToDeal, null);
adapter.notifyItemChanged(holder.getBindingAdapterPosition());
}
/**
* Manage status, this method is also reused in notifications timelines
*
@ -3342,7 +3381,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else {
holder.bindingPixelfed.artReblogPp.setVisibility(View.GONE);
}
boolean remote = timelineType == Timeline.TimeLineEnum.REMOTE || checkRemotely;
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean confirmFav = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION_FAV), false);
MastodonHelper.loadPPMastodon(holder.bindingPixelfed.artPp, statusToDeal.account);
SliderAdapter adapter = new SliderAdapter(statusToDeal);
holder.bindingPixelfed.artMedia.setSliderAdapter(adapter);
@ -3351,6 +3394,83 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.bindingPixelfed.artMedia.setScrollTimeInSec(4);
holder.bindingPixelfed.artMedia.startAutoCycle();
holder.bindingPixelfed.commentNumber.setText(String.valueOf(statusToDeal.replies_count));
holder.bindingPixelfed.actionButtonLike.setActiveImage(R.drawable.ic_heart_filled_24);
holder.bindingPixelfed.actionButtonLike.setInactiveImage(R.drawable.ic_heart_24);
holder.bindingPixelfed.actionButtonLike.setActiveImageTint(R.color.red_color_picker);
holder.bindingPixelfed.actionButtonLike.setChecked(statusToDeal.favourited);
float normalSize = Helper.convertDpToPixel(28, context);
final float scaleIcon = sharedpreferences.getFloat(context.getString(R.string.SET_FONT_SCALE_ICON), 1.1f);
holder.bindingPixelfed.actionButtonLike.setImageSize((int) (normalSize * scaleIcon));
holder.bindingPixelfed.actionButtonLike.setOnLongClickListener(v -> {
if (statusToDeal.visibility.equals("direct") || (statusToDeal.visibility.equals("private"))) {
return true;
}
CrossActionHelper.doCrossAction(context, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, null, statusToDeal);
return true;
});
holder.bindingPixelfed.actionButtonLike.setOnClickListener(v -> {
if (confirmFav) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(context);
if (status.favourited) {
alt_bld.setMessage(context.getString(R.string.favourite_remove));
} else {
alt_bld.setMessage(context.getString(R.string.favourite_add));
}
alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> {
if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show();
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1)
.observe((LifecycleOwner) context, results -> {
if (results != null && results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0);
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id)
.observe((LifecycleOwner) context, _status -> manageAction(context, adapter, holder, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, true));
} else {
Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show();
}
});
} else {
if (status.favourited) {
statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> manageAction(context, adapter, holder, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, false));
} else {
((SparkButton) v).playAnimation();
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> manageAction(context, adapter, holder, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, false));
}
}
dialog.dismiss();
});
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create();
alert.show();
} else {
if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show();
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1)
.observe((LifecycleOwner) context, results -> {
if (results != null && results.statuses != null && !results.statuses.isEmpty()) {
Status fetchedStatus = results.statuses.get(0);
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id)
.observe((LifecycleOwner) context, _status -> manageAction(context, adapter, holder, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, true));
} else {
Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show();
}
});
} else {
if (statusToDeal.favourited) {
statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> manageAction(context, adapter, holder, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, false));
} else {
((SparkButton) v).playAnimation();
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> manageAction(context, adapter, holder, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, false));
}
}
}
});
holder.bindingPixelfed.artUsername.setText(
statusToDeal.account.getSpanDisplayName(context,
new WeakReference<>(holder.bindingPixelfed.artUsername)),

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:strokeColor="@color/white"
android:strokeWidth="2"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/red_color_picker"
android:strokeColor="@color/red_color_picker"
android:strokeWidth="2"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
</vector>

View file

@ -17,6 +17,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:sparkbutton="http://schemas.android.com/apk/res-auto"
android:id="@+id/art_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -49,7 +50,7 @@
android:id="@+id/bottom_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#44000000"
android:background="#77000000"
android:orientation="horizontal"
android:padding="10dp"
app:layout_constraintBottom_toBottomOf="@+id/art_media"
@ -98,10 +99,27 @@
android:textColor="@color/white" />
</androidx.appcompat.widget.LinearLayoutCompat>
<com.varunest.sparkbutton.SparkButton
android:id="@+id/action_button_like"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:contentDescription="@string/favourite_add"
app:activeImage="@drawable/ic_heart_filled_24"
app:animationSpeed="1.5"
app:inactiveImage="@drawable/ic_heart_24"
app:primaryColor="@color/red_color_picker"
app:secondaryColor="@color/red_color_picker"
sparkbutton:iconSize="28dp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/comment_number"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:drawableTint="@color/white"
android:textColor="@color/white"
android:layout_marginStart="10dp"
android:drawableStart="@drawable/ic_baseline_chat_bubble_24"
android:drawablePadding="5dp"