Merge branch 'develop' of https://git.mentality.rip/FWGS/Husky into develop
This commit is contained in:
commit
2f0e79e8fa
@ -165,7 +165,7 @@ dependencies {
|
||||
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
|
||||
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
|
||||
|
||||
implementation "com.github.connyduck:sparkbutton:3.0.0"
|
||||
implementation "com.github.connyduck:sparkbutton:4.0.0"
|
||||
|
||||
implementation "com.github.chrisbanes:PhotoView:2.3.0"
|
||||
|
||||
|
@ -37,7 +37,8 @@ class AccountListActivity : BaseActivity(), HasAndroidInjector {
|
||||
MUTES,
|
||||
FOLLOW_REQUESTS,
|
||||
REBLOGGED,
|
||||
FAVOURITED
|
||||
FAVOURITED,
|
||||
REACTED
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@ -46,6 +47,7 @@ class AccountListActivity : BaseActivity(), HasAndroidInjector {
|
||||
|
||||
val type = intent.getSerializableExtra(EXTRA_TYPE) as Type
|
||||
val id: String? = intent.getStringExtra(EXTRA_ID)
|
||||
val emoji: String? = intent.getStringExtra(EXTRA_EMOJI)
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.apply {
|
||||
@ -57,6 +59,7 @@ class AccountListActivity : BaseActivity(), HasAndroidInjector {
|
||||
Type.FOLLOWS -> setTitle(R.string.title_follows)
|
||||
Type.REBLOGGED -> setTitle(R.string.title_reblogged_by)
|
||||
Type.FAVOURITED -> setTitle(R.string.title_favourited_by)
|
||||
Type.REACTED -> setTitle(String.format(getString(R.string.title_emoji_reacted_by), emoji))
|
||||
}
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
setDisplayShowHomeEnabled(true)
|
||||
@ -64,7 +67,7 @@ class AccountListActivity : BaseActivity(), HasAndroidInjector {
|
||||
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.fragment_container, AccountListFragment.newInstance(type, id))
|
||||
.replace(R.id.fragment_container, AccountListFragment.newInstance(type, id, emoji))
|
||||
.commit()
|
||||
}
|
||||
|
||||
@ -83,13 +86,20 @@ class AccountListActivity : BaseActivity(), HasAndroidInjector {
|
||||
companion object {
|
||||
private const val EXTRA_TYPE = "type"
|
||||
private const val EXTRA_ID = "id"
|
||||
private const val EXTRA_EMOJI = "emoji"
|
||||
|
||||
@JvmStatic
|
||||
fun newIntent(context: Context, type: Type, id: String? = null): Intent {
|
||||
fun newIntent(context: Context, type: Type, id: String?, emoji: String?): Intent {
|
||||
return Intent(context, AccountListActivity::class.java).apply {
|
||||
putExtra(EXTRA_TYPE, type)
|
||||
putExtra(EXTRA_ID, id)
|
||||
putExtra(EXTRA_EMOJI, emoji)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newIntent(context: Context, type: Type, id: String? = null): Intent {
|
||||
return newIntent(context, type, id, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ class PreferencesActivity : BaseActivity(), SharedPreferences.OnSharedPreference
|
||||
|
||||
}
|
||||
"statusTextSize", "absoluteTimeView", "showBotOverlay", "animateGifAvatars",
|
||||
"useBlurhash" -> {
|
||||
"useBlurhash", "showCardsInTimelines", "confirmReblogs" -> {
|
||||
restartActivitiesOnExit = true
|
||||
}
|
||||
"language" -> {
|
||||
|
@ -0,0 +1,39 @@
|
||||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.emoji.widget.EmojiAppCompatButton;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.google.android.flexbox.FlexboxLayoutManager;
|
||||
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
|
||||
public class EmojiReactionViewHolder extends RecyclerView.ViewHolder {
|
||||
public EmojiAppCompatButton emojiReaction;
|
||||
EmojiReactionViewHolder(View view) {
|
||||
super(view);
|
||||
emojiReaction = view.findViewById(R.id.status_emoji_reaction);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
package com.keylesspalace.tusky.adapter;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.emoji.widget.EmojiAppCompatButton;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
public class EmojiReactionsAdapter extends RecyclerView.Adapter<EmojiReactionViewHolder> {
|
||||
private final List<EmojiReaction> reactions;
|
||||
private final StatusActionListener listener;
|
||||
private final String statusId;
|
||||
|
||||
EmojiReactionsAdapter(final List<EmojiReaction> reactions, final StatusActionListener listener, final String statusId) {
|
||||
this.reactions = reactions;
|
||||
this.listener = listener;
|
||||
this.statusId = statusId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmojiReactionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_emoji_reaction, parent, false);
|
||||
return new EmojiReactionViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(EmojiReactionViewHolder holder, int position) {
|
||||
EmojiReaction reaction = reactions.get(position);
|
||||
String str = reaction.getName() + " " + reaction.getCount();
|
||||
|
||||
// no custom emoji yet!
|
||||
holder.emojiReaction.setText(str);
|
||||
holder.emojiReaction.setActivated(reaction.getMe());
|
||||
holder.emojiReaction.setOnClickListener(v -> {
|
||||
listener.onEmojiReactMenu(v, reaction, statusId);
|
||||
});
|
||||
}
|
||||
|
||||
// total number of rows
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return reactions.size();
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.interfaces.LinkListener;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
@ -253,7 +254,9 @@ public class NotificationsAdapter extends RecyclerView.Adapter {
|
||||
mediaPreviewEnabled,
|
||||
statusDisplayOptions.useAbsoluteTime(),
|
||||
statusDisplayOptions.showBotOverlay(),
|
||||
statusDisplayOptions.useBlurhash()
|
||||
statusDisplayOptions.useBlurhash(),
|
||||
CardViewMode.NONE,
|
||||
statusDisplayOptions.confirmReblogs()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,28 +8,37 @@ import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.flexbox.FlexboxLayoutManager;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Attachment.Focus;
|
||||
import com.keylesspalace.tusky.entity.Attachment.MetaData;
|
||||
import com.keylesspalace.tusky.entity.Card;
|
||||
import com.keylesspalace.tusky.entity.Emoji;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.CustomEmojiHelper;
|
||||
import com.keylesspalace.tusky.util.HtmlUtils;
|
||||
import com.keylesspalace.tusky.util.ImageLoadingHelper;
|
||||
@ -87,6 +96,12 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
private TextView pollDescription;
|
||||
private Button pollButton;
|
||||
|
||||
private LinearLayout cardView;
|
||||
private LinearLayout cardInfo;
|
||||
private ImageView cardImage;
|
||||
private TextView cardTitle;
|
||||
private TextView cardDescription;
|
||||
private TextView cardUrl;
|
||||
private PollAdapter pollAdapter;
|
||||
|
||||
private SimpleDateFormat shortSdf;
|
||||
@ -99,6 +114,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
private int avatarRadius24dp;
|
||||
|
||||
private final Drawable mediaPreviewUnloaded;
|
||||
|
||||
private RecyclerView emojiReactionsView;
|
||||
|
||||
protected StatusBaseViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
@ -112,7 +129,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
favouriteButton = itemView.findViewById(R.id.status_favourite);
|
||||
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
|
||||
moreButton = itemView.findViewById(R.id.status_more);
|
||||
|
||||
emojiReactionsView = itemView.findViewById(R.id.status_emoji_reactions);
|
||||
|
||||
float INCREASE_HORIZONTAL_HIT_AREA = 20.0f;
|
||||
|
||||
ViewExtensionsKt.increaseHitArea(replyButton, 0.0f, INCREASE_HORIZONTAL_HIT_AREA);
|
||||
@ -153,6 +171,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
pollDescription = itemView.findViewById(R.id.status_poll_description);
|
||||
pollButton = itemView.findViewById(R.id.status_poll_button);
|
||||
|
||||
cardView = itemView.findViewById(R.id.status_card_view);
|
||||
cardInfo = itemView.findViewById(R.id.card_info);
|
||||
cardImage = itemView.findViewById(R.id.card_image);
|
||||
cardTitle = itemView.findViewById(R.id.card_title);
|
||||
cardDescription = itemView.findViewById(R.id.card_description);
|
||||
cardUrl = itemView.findViewById(R.id.card_link);
|
||||
|
||||
pollAdapter = new PollAdapter();
|
||||
pollOptions.setAdapter(pollAdapter);
|
||||
pollOptions.setLayoutManager(new LinearLayoutManager(pollOptions.getContext()));
|
||||
@ -200,7 +225,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
contentWarningDescription.setVisibility(View.VISIBLE);
|
||||
contentWarningButton.setVisibility(View.VISIBLE);
|
||||
setContentWarningButtonText(expanded);
|
||||
contentWarningButton.setOnClickListener( view -> {
|
||||
contentWarningButton.setOnClickListener(view -> {
|
||||
contentWarningDescription.invalidate();
|
||||
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onExpandedChange(!expanded, getAdapterPosition());
|
||||
@ -218,7 +243,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
}
|
||||
|
||||
private void setContentWarningButtonText(boolean expanded) {
|
||||
if(expanded) {
|
||||
if (expanded) {
|
||||
contentWarningButton.setText(R.string.status_content_warning_show_less);
|
||||
} else {
|
||||
contentWarningButton.setText(R.string.status_content_warning_show_more);
|
||||
@ -601,7 +626,9 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
sensitiveMediaShow.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
protected void setupButtons(final StatusActionListener listener, final String accountId) {
|
||||
protected void setupButtons(final StatusActionListener listener, final String accountId,
|
||||
final String statusContent,
|
||||
StatusDisplayOptions statusDisplayOptions) {
|
||||
|
||||
avatar.setOnClickListener(v -> listener.onViewAccount(accountId));
|
||||
replyButton.setOnClickListener(v -> {
|
||||
@ -614,7 +641,13 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
reblogButton.setEventListener((button, buttonState) -> {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onReblog(buttonState, position);
|
||||
listener.onReblog(!buttonState, position);
|
||||
}
|
||||
if (statusDisplayOptions.confirmReblogs()) {
|
||||
showConfirmReblogDialog(listener, statusContent, buttonState, position);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -622,15 +655,17 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
favouriteButton.setEventListener((button, buttonState) -> {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onFavourite(buttonState, position);
|
||||
listener.onFavourite(!buttonState, position);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
bookmarkButton.setEventListener((button, buttonState) -> {
|
||||
int position = getAdapterPosition();
|
||||
if (position != RecyclerView.NO_POSITION) {
|
||||
listener.onBookmark(buttonState, position);
|
||||
listener.onBookmark(!buttonState, position);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
moreButton.setOnClickListener(v -> {
|
||||
@ -654,6 +689,32 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
itemView.setOnClickListener(viewThreadListener);
|
||||
}
|
||||
|
||||
private void showConfirmReblogDialog(StatusActionListener listener,
|
||||
String statusContent,
|
||||
boolean buttonState,
|
||||
int position) {
|
||||
int okButtonTextId = buttonState ? R.string.action_unreblog : R.string.action_reblog;
|
||||
new AlertDialog.Builder(reblogButton.getContext())
|
||||
.setMessage(statusContent)
|
||||
.setPositiveButton(okButtonTextId, (__, ___) -> {
|
||||
listener.onReblog(!buttonState, position);
|
||||
if (!buttonState) {
|
||||
// Play animation only when it's reblog, not unreblog
|
||||
reblogButton.playAnimation();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
private void setEmojiReactions(@Nullable List<EmojiReaction> reactions, final StatusActionListener listener, final String statusId) {
|
||||
if(emojiReactionsView != null && reactions != null && reactions.size() > 0) {
|
||||
emojiReactionsView.setVisibility(View.VISIBLE);
|
||||
FlexboxLayoutManager lm = new FlexboxLayoutManager(emojiReactionsView.getContext());
|
||||
emojiReactionsView.setLayoutManager(lm);
|
||||
emojiReactionsView.setAdapter(new EmojiReactionsAdapter(reactions, listener, statusId));
|
||||
}
|
||||
}
|
||||
|
||||
public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
|
||||
StatusDisplayOptions statusDisplayOptions) {
|
||||
this.setupWithStatus(status, listener, statusDisplayOptions, null);
|
||||
@ -694,12 +755,19 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
hideSensitiveMediaWarning();
|
||||
}
|
||||
|
||||
setupButtons(listener, status.getSenderId());
|
||||
if (cardView != null) {
|
||||
setupCard(status, statusDisplayOptions.cardViewMode());
|
||||
}
|
||||
|
||||
setupButtons(listener, status.getSenderId(), status.getContent().toString(),
|
||||
statusDisplayOptions);
|
||||
setRebloggingEnabled(status.getRebloggingEnabled(), status.getVisibility());
|
||||
|
||||
setSpoilerAndContent(status.isExpanded(), status.getContent(), status.getSpoilerText(), status.getMentions(), status.getStatusEmojis(), status.getPoll(), statusDisplayOptions, listener);
|
||||
|
||||
setDescriptionForStatus(status, statusDisplayOptions);
|
||||
|
||||
setEmojiReactions(status.getEmojiReactions(), listener, status.getId());
|
||||
|
||||
// Workaround for RecyclerView 1.0.0 / androidx.core 1.0.0
|
||||
// RecyclerView tries to set AccessibilityDelegateCompat to null
|
||||
@ -922,6 +990,80 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||
return pollDescription.getContext().getString(R.string.poll_info_format, votesText, pollDurationInfo);
|
||||
}
|
||||
|
||||
protected void setupCard(StatusViewData.Concrete status, CardViewMode cardViewMode) {
|
||||
if (cardViewMode != CardViewMode.NONE && status.getAttachments().size() == 0 && status.getCard() != null && !TextUtils.isEmpty(status.getCard().getUrl())) {
|
||||
final Card card = status.getCard();
|
||||
cardView.setVisibility(View.VISIBLE);
|
||||
cardTitle.setText(card.getTitle());
|
||||
if (TextUtils.isEmpty(card.getDescription()) && TextUtils.isEmpty(card.getAuthorName())) {
|
||||
cardDescription.setVisibility(View.GONE);
|
||||
} else {
|
||||
cardDescription.setVisibility(View.VISIBLE);
|
||||
if (TextUtils.isEmpty(card.getDescription())) {
|
||||
cardDescription.setText(card.getAuthorName());
|
||||
} else {
|
||||
cardDescription.setText(card.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
cardUrl.setText(card.getUrl());
|
||||
|
||||
if (!TextUtils.isEmpty(card.getImage())) {
|
||||
|
||||
int topLeftRadius = 0;
|
||||
int topRightRadius = 0;
|
||||
int bottomRightRadius = 0;
|
||||
int bottomLeftRadius = 0;
|
||||
|
||||
int radius = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_radius);
|
||||
|
||||
if (card.getWidth() > card.getHeight()) {
|
||||
cardView.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
cardImage.getLayoutParams().height = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_vertical_height);
|
||||
cardImage.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
topLeftRadius = radius;
|
||||
topRightRadius = radius;
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.getLayoutParams().width = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_horizontal_width);
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
topLeftRadius = radius;
|
||||
bottomLeftRadius = radius;
|
||||
}
|
||||
|
||||
|
||||
Glide.with(cardImage)
|
||||
.load(card.getImage())
|
||||
.transform(
|
||||
new CenterCrop(),
|
||||
new GranularRoundedCorners(topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius)
|
||||
)
|
||||
.into(cardImage);
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.getLayoutParams().width = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_horizontal_width);
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.setImageResource(R.drawable.card_image_placeholder);
|
||||
}
|
||||
|
||||
cardView.setOnClickListener(v -> LinkHelper.openLink(card.getUrl(), v.getContext()));
|
||||
cardView.setClipToOutline(true);
|
||||
} else {
|
||||
cardView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private static String formatDuration(double durationInSeconds) {
|
||||
int seconds = (int) Math.round(durationInSeconds) % 60;
|
||||
int minutes = (int) durationInSeconds % 3600 / 60;
|
||||
|
@ -4,13 +4,9 @@ import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.util.Log;
|
||||
@ -20,14 +16,11 @@ import androidx.emoji.widget.EmojiAppCompatButton;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.google.android.flexbox.FlexboxLayoutManager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.entity.Card;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||
import com.keylesspalace.tusky.viewdata.StatusViewData;
|
||||
@ -39,29 +32,13 @@ import java.util.Date;
|
||||
class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||
private TextView reblogs;
|
||||
private TextView favourites;
|
||||
private LinearLayout cardView;
|
||||
private LinearLayout cardInfo;
|
||||
private ImageView cardImage;
|
||||
private TextView cardTitle;
|
||||
private TextView cardDescription;
|
||||
private TextView cardUrl;
|
||||
private View infoDivider;
|
||||
private RecyclerView emojiReactionsView;
|
||||
|
||||
StatusDetailedViewHolder(View view) {
|
||||
super(view);
|
||||
reblogs = view.findViewById(R.id.status_reblogs);
|
||||
favourites = view.findViewById(R.id.status_favourites);
|
||||
cardView = view.findViewById(R.id.card_view);
|
||||
cardInfo = view.findViewById(R.id.card_info);
|
||||
cardImage = view.findViewById(R.id.card_image);
|
||||
cardTitle = view.findViewById(R.id.card_title);
|
||||
cardDescription = view.findViewById(R.id.card_description);
|
||||
cardUrl = view.findViewById(R.id.card_link);
|
||||
infoDivider = view.findViewById(R.id.status_info_divider);
|
||||
emojiReactionsView = view.findViewById(R.id.status_emoji_reactions);
|
||||
|
||||
cardView.setClipToOutline(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,71 +106,17 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||
}
|
||||
}
|
||||
|
||||
private class EmojiReactionViewHolder extends RecyclerView.ViewHolder {
|
||||
public EmojiAppCompatButton emojiReaction;
|
||||
EmojiReactionViewHolder(View view) {
|
||||
super(view);
|
||||
emojiReaction = view.findViewById(R.id.status_emoji_reaction);
|
||||
}
|
||||
}
|
||||
|
||||
private class EmojiReactionsAdapter extends RecyclerView.Adapter<EmojiReactionViewHolder> {
|
||||
private List<EmojiReaction> reactions;
|
||||
|
||||
EmojiReactionsAdapter(List<EmojiReaction> reactions) {
|
||||
this.reactions = reactions;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmojiReactionViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_emoji_reaction, parent, false);
|
||||
return new EmojiReactionViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(EmojiReactionViewHolder holder, int position) {
|
||||
EmojiReaction reaction = reactions.get(position);
|
||||
String str = reaction.getName() + " " + reaction.getCount();
|
||||
|
||||
// no custom emoji yet!
|
||||
holder.emojiReaction.setText(str);
|
||||
holder.emojiReaction.setActivated(reaction.getMe());
|
||||
holder.emojiReaction.setOnClickListener(v -> {});
|
||||
}
|
||||
|
||||
// total number of rows
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return reactions.size();
|
||||
}
|
||||
}
|
||||
|
||||
private void setEmojiReactions(@Nullable List<EmojiReaction> reactions) {
|
||||
if(reactions != null) {
|
||||
emojiReactionsView.setVisibility(View.VISIBLE);
|
||||
FlexboxLayoutManager lm = new FlexboxLayoutManager(emojiReactionsView.getContext());
|
||||
// lm.setFlexDirection(FlexDirection.COLUMN);
|
||||
// StaggeredGridLayoutManager.HORIZONTAL);
|
||||
// lm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS);
|
||||
emojiReactionsView.setLayoutManager(lm);
|
||||
emojiReactionsView.setAdapter(new EmojiReactionsAdapter(reactions));
|
||||
//emojiReactionsView.setLayoutManager StaggeredGridLayoutManager
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupWithStatus(final StatusViewData.Concrete status,
|
||||
final StatusActionListener listener,
|
||||
StatusDisplayOptions statusDisplayOptions,
|
||||
@Nullable Object payloads) {
|
||||
super.setupWithStatus(status, listener, statusDisplayOptions, payloads);
|
||||
setupCard(status, CardViewMode.FULL_WIDTH); // Always show card for detailed status
|
||||
if (payloads == null) {
|
||||
setReblogAndFavCount(status.getReblogsCount(), status.getFavouritesCount(), listener);
|
||||
|
||||
setApplication(status.getApplication());
|
||||
setEmojiReactions(status.getEmojiReactions());
|
||||
View.OnLongClickListener longClickListener = view -> {
|
||||
TextView textView = (TextView) view;
|
||||
ClipboardManager clipboard = (ClipboardManager) view.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
@ -207,82 +130,6 @@ class StatusDetailedViewHolder extends StatusBaseViewHolder {
|
||||
|
||||
content.setOnLongClickListener(longClickListener);
|
||||
contentWarningDescription.setOnLongClickListener(longClickListener);
|
||||
|
||||
if (status.getAttachments().size() == 0 && status.getCard() != null && !TextUtils.isEmpty(status.getCard().getUrl())) {
|
||||
final Card card = status.getCard();
|
||||
cardView.setVisibility(View.VISIBLE);
|
||||
cardTitle.setText(card.getTitle());
|
||||
if (TextUtils.isEmpty(card.getDescription()) && TextUtils.isEmpty(card.getAuthorName())) {
|
||||
cardDescription.setVisibility(View.GONE);
|
||||
} else {
|
||||
cardDescription.setVisibility(View.VISIBLE);
|
||||
if (TextUtils.isEmpty(card.getDescription())) {
|
||||
cardDescription.setText(card.getAuthorName());
|
||||
} else {
|
||||
cardDescription.setText(card.getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
cardUrl.setText(card.getUrl());
|
||||
|
||||
if (!TextUtils.isEmpty(card.getImage())) {
|
||||
|
||||
int topLeftRadius = 0;
|
||||
int topRightRadius = 0;
|
||||
int bottomRightRadius = 0;
|
||||
int bottomLeftRadius = 0;
|
||||
|
||||
int radius = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_radius);
|
||||
|
||||
if (card.getWidth() > card.getHeight()) {
|
||||
cardView.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
cardImage.getLayoutParams().height = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_vertical_height);
|
||||
cardImage.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
topLeftRadius = radius;
|
||||
topRightRadius = radius;
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.getLayoutParams().width = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_horizontal_width);
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
topLeftRadius = radius;
|
||||
bottomLeftRadius = radius;
|
||||
}
|
||||
|
||||
|
||||
Glide.with(cardImage)
|
||||
.load(card.getImage())
|
||||
.transform(
|
||||
new CenterCrop(),
|
||||
new GranularRoundedCorners(topLeftRadius, topRightRadius, bottomRightRadius, bottomLeftRadius)
|
||||
)
|
||||
.into(cardImage);
|
||||
|
||||
} else {
|
||||
cardView.setOrientation(LinearLayout.HORIZONTAL);
|
||||
cardImage.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
cardImage.getLayoutParams().width = cardImage.getContext().getResources()
|
||||
.getDimensionPixelSize(R.dimen.card_image_horizontal_width);
|
||||
cardInfo.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
cardInfo.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
cardImage.setImageResource(R.drawable.card_image_placeholder);
|
||||
|
||||
}
|
||||
|
||||
cardView.setOnClickListener(v -> LinkHelper.openLink(card.getUrl(), v.getContext()));
|
||||
|
||||
} else {
|
||||
cardView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
setStatusVisibility(status.getVisibility());
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,9 @@ public final class TimelineAdapter extends RecyclerView.Adapter {
|
||||
mediaPreviewEnabled,
|
||||
statusDisplayOptions.useAbsoluteTime(),
|
||||
statusDisplayOptions.showBotOverlay(),
|
||||
statusDisplayOptions.useBlurhash()
|
||||
statusDisplayOptions.useBlurhash(),
|
||||
statusDisplayOptions.cardViewMode(),
|
||||
statusDisplayOptions.confirmReblogs()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ data class FavoriteEvent(val statusId: String, val favourite: Boolean) : Dispatc
|
||||
data class ReblogEvent(val statusId: String, val reblog: Boolean) : Dispatchable
|
||||
data class BookmarkEvent(val statusId: String, val bookmark: Boolean) : Dispatchable
|
||||
data class MuteStatusEvent(val statusId: String, val mute: Boolean) : Dispatchable
|
||||
data class EmojiReactEvent(val statusId: String, val reacted: Boolean, val emoji: String) : Dispatchable
|
||||
data class EmojiReactEvent(val newStatus: Status) : Dispatchable
|
||||
data class UnfollowEvent(val accountId: String) : Dispatchable
|
||||
data class BlockEvent(val accountId: String) : Dispatchable
|
||||
data class MuteEvent(val accountId: String) : Dispatchable
|
||||
|
@ -104,7 +104,8 @@ public class ConversationViewHolder extends StatusBaseViewHolder {
|
||||
hideSensitiveMediaWarning();
|
||||
}
|
||||
|
||||
setupButtons(listener, account.getId());
|
||||
setupButtons(listener, account.getId(), status.getContent().toString(),
|
||||
statusDisplayOptions);
|
||||
|
||||
setSpoilerAndContent(status.getExpanded(), status.getContent(), status.getSpoilerText(),
|
||||
status.getMentions(), status.getEmojis(),
|
||||
|
@ -36,10 +36,7 @@ import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.util.NetworkState
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.*
|
||||
import kotlinx.android.synthetic.main.fragment_timeline.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -68,7 +65,9 @@ class ConversationsFragment : SFragment(), StatusActionListener, Injectable, Res
|
||||
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||
cardViewMode = CardViewMode.NONE,
|
||||
confirmReblogs = preferences.getBoolean("confirmReblogs", true)
|
||||
)
|
||||
|
||||
|
||||
|
@ -43,10 +43,7 @@ import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.util.ThemeUtils
|
||||
import com.keylesspalace.tusky.util.hide
|
||||
import com.keylesspalace.tusky.util.show
|
||||
import com.keylesspalace.tusky.util.*
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
import kotlinx.android.synthetic.main.fragment_report_statuses.*
|
||||
import javax.inject.Inject
|
||||
@ -119,7 +116,9 @@ class ReportStatusesFragment : Fragment(), Injectable, AdapterHandler {
|
||||
mediaPreviewEnabled = accountManager.activeAccount?.mediaPreviewEnabled ?: true,
|
||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||
showBotOverlay = false,
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||
cardViewMode = CardViewMode.NONE,
|
||||
confirmReblogs = preferences.getBoolean("confirmReblogs", true)
|
||||
)
|
||||
|
||||
adapter = StatusesAdapter(statusDisplayOptions,
|
||||
|
@ -51,6 +51,7 @@ import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.util.CardViewMode
|
||||
import com.keylesspalace.tusky.util.NetworkState
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||
@ -70,7 +71,7 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||
get() = viewModel.statuses
|
||||
|
||||
private val searchAdapter
|
||||
get() = super.adapter as SearchStatusesAdapter
|
||||
get() = super.adapter as SearchStatusesAdapter
|
||||
|
||||
override fun createAdapter(): PagedListAdapter<Pair<Status, StatusViewData.Concrete>, *> {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(searchRecyclerView.context)
|
||||
@ -79,7 +80,9 @@ class SearchStatusesFragment : SearchFragment<Pair<Status, StatusViewData.Concre
|
||||
mediaPreviewEnabled = viewModel.mediaPreviewEnabled,
|
||||
useAbsoluteTime = preferences.getBoolean("absoluteTimeView", false),
|
||||
showBotOverlay = preferences.getBoolean("showBotOverlay", true),
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true)
|
||||
useBlurhash = preferences.getBoolean("useBlurhash", true),
|
||||
cardViewMode = CardViewMode.NONE,
|
||||
confirmReblogs = preferences.getBoolean("confirmReblogs", true)
|
||||
)
|
||||
|
||||
searchRecyclerView.addItemDecoration(DividerItemDecoration(searchRecyclerView.context, DividerItemDecoration.VERTICAL))
|
||||
|
@ -145,8 +145,8 @@ data class Status(
|
||||
|
||||
fun getEmojiReactions(): List<EmojiReaction>? {
|
||||
return pleroma?.emojiReactions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun getEditableText(): String {
|
||||
val builder = SpannableStringBuilder(content)
|
||||
for (span in content.getSpans(0, content.length, URLSpan::class.java)) {
|
||||
|
@ -31,8 +31,7 @@ import com.keylesspalace.tusky.BaseActivity
|
||||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.adapter.*
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.Account
|
||||
import com.keylesspalace.tusky.entity.Relationship
|
||||
import com.keylesspalace.tusky.entity.*
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink
|
||||
@ -58,6 +57,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
||||
|
||||
private lateinit var type: Type
|
||||
private var id: String? = null
|
||||
private var emojiReaction: String? = null
|
||||
|
||||
private lateinit var scrollListener: EndlessOnScrollListener
|
||||
private lateinit var adapter: AccountAdapter
|
||||
@ -68,6 +68,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
||||
super.onCreate(savedInstanceState)
|
||||
type = arguments?.getSerializable(ARG_TYPE) as Type
|
||||
id = arguments?.getString(ARG_ID)
|
||||
emojiReaction = arguments?.getString(ARG_EMOJI)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
@ -273,11 +274,22 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
||||
val statusId = requireId(type, id)
|
||||
api.statusFavouritedBy(statusId, fromId)
|
||||
}
|
||||
Type.REACTED -> {
|
||||
// HACKHACK: make compiler happy
|
||||
val statusId = requireId(type, id)
|
||||
api.statusFavouritedBy(statusId, fromId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireId(type: Type, id: String?): String {
|
||||
return requireNotNull(id) { "id must not be null for type "+type.name }
|
||||
|
||||
private fun requireId(type: Type, id: String?, name: String = "id"): String {
|
||||
return requireNotNull(id) { name+" must not be null for type "+type.name }
|
||||
}
|
||||
|
||||
private fun getEmojiReactionFetchCall(): Single<Response<List<EmojiReaction>>> {
|
||||
val statusId = requireId(type, id)
|
||||
val emoji = requireId(type, emojiReaction, "emoji")
|
||||
return api.statusReactedBy(statusId, emoji)
|
||||
}
|
||||
|
||||
private fun fetchAccounts(fromId: String? = null) {
|
||||
@ -289,8 +301,25 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
||||
if (fromId != null) {
|
||||
recyclerView.post { adapter.setBottomLoading(true) }
|
||||
}
|
||||
|
||||
if(type == Type.REACTED) {
|
||||
getEmojiReactionFetchCall()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(from(this, Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe({ response ->
|
||||
val emojiReaction = response.body()
|
||||
|
||||
getFetchCallByListType(fromId)
|
||||
if (response.isSuccessful && emojiReaction != null && emojiReaction.size > 0 && emojiReaction.get(0).accounts != null) {
|
||||
val linkHeader = response.headers()["Link"]
|
||||
onFetchAccountsSuccess(emojiReaction.get(0).accounts!!, linkHeader)
|
||||
} else {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
}
|
||||
}, {throwable ->
|
||||
onFetchAccountsFailure(throwable)
|
||||
})
|
||||
} else {
|
||||
getFetchCallByListType(fromId)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(from(this, Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe({ response ->
|
||||
@ -305,7 +334,7 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
||||
}, {throwable ->
|
||||
onFetchAccountsFailure(throwable)
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFetchAccountsSuccess(accounts: List<Account>, linkHeader: String?) {
|
||||
@ -361,12 +390,14 @@ class AccountListFragment : BaseFragment(), AccountActionListener, Injectable {
|
||||
private const val TAG = "AccountList" // logging tag
|
||||
private const val ARG_TYPE = "type"
|
||||
private const val ARG_ID = "id"
|
||||
private const val ARG_EMOJI = "emoji"
|
||||
|
||||
fun newInstance(type: Type, id: String? = null): AccountListFragment {
|
||||
fun newInstance(type: Type, id: String? = null, emoji: String? = null): AccountListFragment {
|
||||
return AccountListFragment().apply {
|
||||
arguments = Bundle(2).apply {
|
||||
arguments = Bundle(3).apply {
|
||||
putSerializable(ARG_TYPE, type)
|
||||
putString(ARG_ID, id)
|
||||
putString(ARG_EMOJI, emoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.Either;
|
||||
import com.keylesspalace.tusky.util.HttpHeaderLink;
|
||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
||||
@ -239,7 +240,9 @@ public class NotificationsFragment extends SFragment implements
|
||||
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
||||
preferences.getBoolean("absoluteTimeView", false),
|
||||
preferences.getBoolean("showBotOverlay", true),
|
||||
preferences.getBoolean("useBlurhash", true)
|
||||
preferences.getBoolean("useBlurhash", true),
|
||||
CardViewMode.NONE,
|
||||
preferences.getBoolean("confirmReblogs", true)
|
||||
);
|
||||
|
||||
adapter = new NotificationsAdapter(accountManager.getActiveAccount().getAccountId(),
|
||||
|
@ -43,6 +43,7 @@ import androidx.lifecycle.Lifecycle;
|
||||
import com.keylesspalace.tusky.BaseActivity;
|
||||
import com.keylesspalace.tusky.BottomSheetActivity;
|
||||
import com.keylesspalace.tusky.MainActivity;
|
||||
import com.keylesspalace.tusky.AccountListActivity;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.PostLookupFallbackBehavior;
|
||||
import com.keylesspalace.tusky.ViewMediaActivity;
|
||||
@ -57,9 +58,11 @@ import com.keylesspalace.tusky.entity.Attachment;
|
||||
import com.keylesspalace.tusky.entity.Filter;
|
||||
import com.keylesspalace.tusky.entity.PollOption;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.network.TimelineCases;
|
||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
@ -172,6 +175,33 @@ public abstract class SFragment extends BaseFragment implements Injectable {
|
||||
Intent intent = ComposeActivity.startIntent(getContext(), composeOptions);
|
||||
getActivity().startActivity(intent);
|
||||
}
|
||||
|
||||
protected void emojiReactMenu(@NonNull final String statusId, @NonNull final EmojiReaction reaction, View view, final StatusActionListener listener) {
|
||||
PopupMenu popup = new PopupMenu(getContext(), view);
|
||||
|
||||
popup.inflate(R.menu.emoji_reaction_more);
|
||||
Menu menu = popup.getMenu();
|
||||
menu.findItem(R.id.emoji_react).setVisible(!reaction.getMe());
|
||||
menu.findItem(R.id.emoji_unreact).setVisible(reaction.getMe());
|
||||
|
||||
popup.setOnMenuItemClickListener(item -> {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.emoji_react:
|
||||
listener.onEmojiReact(true, reaction.getName(), statusId);
|
||||
return true;
|
||||
case R.id.emoji_unreact:
|
||||
listener.onEmojiReact(false, reaction.getName(), statusId);
|
||||
return true;
|
||||
case R.id.emoji_reacted_by:
|
||||
Intent intent = AccountListActivity.newIntent(getContext(), AccountListActivity.Type.REACTED, statusId, reaction.getName());
|
||||
((BaseActivity) getActivity()).startActivityWithSlideInAnimation(intent);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
popup.show();
|
||||
}
|
||||
|
||||
protected void more(@NonNull final Status status, View view, final int position) {
|
||||
final String id = status.getActionableId();
|
||||
|
@ -51,9 +51,7 @@ import com.keylesspalace.tusky.adapter.TimelineAdapter;
|
||||
import com.keylesspalace.tusky.appstore.*;
|
||||
import com.keylesspalace.tusky.db.AccountManager;
|
||||
import com.keylesspalace.tusky.di.Injectable;
|
||||
import com.keylesspalace.tusky.entity.Filter;
|
||||
import com.keylesspalace.tusky.entity.Poll;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.*;
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity;
|
||||
import com.keylesspalace.tusky.interfaces.RefreshableFragment;
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment;
|
||||
@ -62,6 +60,7 @@ import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.repository.Placeholder;
|
||||
import com.keylesspalace.tusky.repository.TimelineRepository;
|
||||
import com.keylesspalace.tusky.repository.TimelineRequestMode;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.Either;
|
||||
import com.keylesspalace.tusky.util.LinkHelper;
|
||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
||||
@ -217,7 +216,11 @@ public class TimelineFragment extends SFragment implements
|
||||
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
||||
preferences.getBoolean("absoluteTimeView", false),
|
||||
preferences.getBoolean("showBotOverlay", true),
|
||||
preferences.getBoolean("useBlurhash", true)
|
||||
preferences.getBoolean("useBlurhash", true),
|
||||
preferences.getBoolean("showCardsInTimelines", false) ?
|
||||
CardViewMode.INDENTED :
|
||||
CardViewMode.NONE,
|
||||
preferences.getBoolean("confirmReblogs", true)
|
||||
);
|
||||
adapter = new TimelineAdapter(dataSource, statusDisplayOptions, this);
|
||||
|
||||
@ -528,6 +531,8 @@ public class TimelineFragment extends SFragment implements
|
||||
handleStatusComposeEvent(status);
|
||||
} else if (event instanceof PreferenceChangedEvent) {
|
||||
onPreferenceChanged(((PreferenceChangedEvent) event).getPreferenceKey());
|
||||
} else if (event instanceof EmojiReactEvent) {
|
||||
handleEmojiReactEvent((EmojiReactEvent)event);
|
||||
}
|
||||
});
|
||||
eventRegistered = true;
|
||||
@ -576,6 +581,10 @@ public class TimelineFragment extends SFragment implements
|
||||
@Override
|
||||
public void onReblog(final boolean reblog, final int position) {
|
||||
final Status status = statuses.get(position).asRight();
|
||||
doReblog(reblog, position, status);
|
||||
}
|
||||
|
||||
private void doReblog(boolean reblog, int position, Status status) {
|
||||
timelineCases.reblog(status, reblog)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this, Lifecycle.Event.ON_DESTROY)))
|
||||
@ -1489,4 +1498,48 @@ public class TimelineFragment extends SFragment implements
|
||||
else
|
||||
isNeedRefresh = true;
|
||||
}
|
||||
|
||||
private void setEmojiReactionForStatus(int position, Status newStatus) {
|
||||
StatusViewData newViewData = ViewDataUtils.statusToViewData(newStatus, false, false);
|
||||
statuses.setPairedItem(position, newViewData);
|
||||
updateAdapter();
|
||||
}
|
||||
|
||||
private void setEmojiReactForStatus(int position, Status status, Status newStatus) {
|
||||
Pair<StatusViewData.Concrete, Integer> actual =
|
||||
findStatusAndPosition(position, status);
|
||||
if (actual == null) return;
|
||||
|
||||
setEmojiReactionForStatus(actual.second, newStatus);
|
||||
}
|
||||
|
||||
public void handleEmojiReactEvent(EmojiReactEvent event) {
|
||||
int pos = findStatusOrReblogPositionById(event.getNewStatus().getActionableId());
|
||||
if (pos < 0) return;
|
||||
Status status = statuses.get(pos).asRight();
|
||||
setEmojiReactForStatus(pos, status, event.getNewStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmojiReact(final boolean react, final String emoji, final String statusId) {
|
||||
int position = findStatusOrReblogPositionById(statusId);
|
||||
if (position < 0) return;
|
||||
|
||||
timelineCases.react(emoji, statusId, react)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this)))
|
||||
.subscribe(
|
||||
(newStatus) -> setEmojiReactionForStatus(position, newStatus),
|
||||
(t) -> Log.d(TAG,
|
||||
"Failed to react with " + emoji + " on status: " + statusId, t)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) {
|
||||
super.emojiReactMenu(statusId, emoji, view, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,20 +44,12 @@ import com.keylesspalace.tusky.BuildConfig;
|
||||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.ViewThreadActivity;
|
||||
import com.keylesspalace.tusky.adapter.ThreadAdapter;
|
||||
import com.keylesspalace.tusky.appstore.BlockEvent;
|
||||
import com.keylesspalace.tusky.appstore.BookmarkEvent;
|
||||
import com.keylesspalace.tusky.appstore.EventHub;
|
||||
import com.keylesspalace.tusky.appstore.FavoriteEvent;
|
||||
import com.keylesspalace.tusky.appstore.ReblogEvent;
|
||||
import com.keylesspalace.tusky.appstore.StatusComposedEvent;
|
||||
import com.keylesspalace.tusky.appstore.StatusDeletedEvent;
|
||||
import com.keylesspalace.tusky.appstore.*;
|
||||
import com.keylesspalace.tusky.di.Injectable;
|
||||
import com.keylesspalace.tusky.entity.Filter;
|
||||
import com.keylesspalace.tusky.entity.Poll;
|
||||
import com.keylesspalace.tusky.entity.Status;
|
||||
import com.keylesspalace.tusky.entity.StatusContext;
|
||||
import com.keylesspalace.tusky.entity.*;
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener;
|
||||
import com.keylesspalace.tusky.network.MastodonApi;
|
||||
import com.keylesspalace.tusky.util.CardViewMode;
|
||||
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
|
||||
import com.keylesspalace.tusky.util.PairedList;
|
||||
import com.keylesspalace.tusky.util.StatusDisplayOptions;
|
||||
@ -131,7 +123,11 @@ public final class ViewThreadFragment extends SFragment implements
|
||||
accountManager.getActiveAccount().getMediaPreviewEnabled(),
|
||||
preferences.getBoolean("absoluteTimeView", false),
|
||||
preferences.getBoolean("showBotOverlay", true),
|
||||
preferences.getBoolean("useBlurhash", true)
|
||||
preferences.getBoolean("useBlurhash", true),
|
||||
preferences.getBoolean("showCardsInTimelines", false) ?
|
||||
CardViewMode.INDENTED :
|
||||
CardViewMode.NONE,
|
||||
preferences.getBoolean("confirmReblogs", true)
|
||||
);
|
||||
adapter = new ThreadAdapter(statusDisplayOptions, this);
|
||||
}
|
||||
@ -194,6 +190,8 @@ public final class ViewThreadFragment extends SFragment implements
|
||||
handleStatusComposedEvent((StatusComposedEvent) event);
|
||||
} else if (event instanceof StatusDeletedEvent) {
|
||||
handleStatusDeletedEvent((StatusDeletedEvent) event);
|
||||
} else if (event instanceof EmojiReactEvent) {
|
||||
handleEmojiReactEvent((EmojiReactEvent)event);
|
||||
}
|
||||
});
|
||||
|
||||
@ -742,4 +740,41 @@ public final class ViewThreadFragment extends SFragment implements
|
||||
protected void refreshAfterApplyingFilters() {
|
||||
onRefresh();
|
||||
}
|
||||
|
||||
private void setEmojiReactionForStatus(int position, Status status) {
|
||||
StatusViewData.Concrete newViewData = ViewDataUtils.statusToViewData(status, false, false);
|
||||
|
||||
statuses.setPairedItem(position, newViewData);
|
||||
adapter.setItem(position, newViewData, true);
|
||||
}
|
||||
|
||||
public void handleEmojiReactEvent(EmojiReactEvent event) {
|
||||
Pair<Integer, Status> posAndStatus = findStatusAndPos(event.getNewStatus().getActionableId());
|
||||
if (posAndStatus == null) return;
|
||||
setEmojiReactionForStatus(posAndStatus.first, event.getNewStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmojiReact(final boolean react, final String emoji, final String statusId) {
|
||||
Pair<Integer, Status> statusAndPos = findStatusAndPos(statusId);
|
||||
|
||||
if(statusAndPos == null)
|
||||
return;
|
||||
int position = statusAndPos.first;
|
||||
|
||||
timelineCases.react(emoji, statusId, react)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.as(autoDisposable(from(this)))
|
||||
.subscribe(
|
||||
(newStatus) -> setEmojiReactionForStatus(position, newStatus),
|
||||
(t) -> Log.d(TAG,
|
||||
"Failed to react with " + emoji + " on status: " + statusId, t)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) {
|
||||
super.emojiReactMenu(statusId, emoji, view, this);
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package com.keylesspalace.tusky.interfaces;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.List;
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -64,6 +65,7 @@ public interface StatusActionListener extends LinkListener {
|
||||
void onVoteInPoll(int position, @NonNull List<Integer> choices);
|
||||
|
||||
default void onMute(int position, boolean isMuted) {}
|
||||
default void onEmojiReact(final boolean react, final String emoji, final int position) {};
|
||||
default void onEmojiReact(final boolean react, final String emoji, final String statusId) {};
|
||||
default void onEmojiReactMenu(@NonNull View view, final EmojiReaction emoji, final String statusId) {};
|
||||
|
||||
}
|
||||
|
@ -588,19 +588,20 @@ interface MastodonApi {
|
||||
fun getNodeinfo(@Url url: String) : Single<NodeInfo>
|
||||
|
||||
@PUT("api/v1/pleroma/statuses/{id}/reactions/{emoji}")
|
||||
fun reactWithEmoji(
|
||||
@Path("id") statusId: String,
|
||||
@Path("emoji") emoji: String
|
||||
): Single<Status>
|
||||
|
||||
@DELETE("api/v1/pleroma/statuses/{id}/reactions/{emoji}")
|
||||
fun unreactWithEmoji(
|
||||
@Path("id") statusId: String,
|
||||
@Path("emoji") emoji: String
|
||||
): Single<Status>
|
||||
|
||||
@GET("api/v1/pleroma/statuses/{id}/reactions")
|
||||
fun statusReactedBy(
|
||||
@Path("id") statusId: String
|
||||
): Single<List<EmojiReaction>>
|
||||
fun reactWithEmoji(
|
||||
@Path("id") statusId: String,
|
||||
@Path("emoji") emoji: String
|
||||
): Single<Status>
|
||||
|
||||
@DELETE("api/v1/pleroma/statuses/{id}/reactions/{emoji}")
|
||||
fun unreactWithEmoji(
|
||||
@Path("id") statusId: String,
|
||||
@Path("emoji") emoji: String
|
||||
): Single<Status>
|
||||
|
||||
@GET("api/v1/pleroma/statuses/{id}/reactions/{emoji}")
|
||||
fun statusReactedBy(
|
||||
@Path("id") statusId: String,
|
||||
@Path("emoji") emoji: String
|
||||
): Single<Response<List<EmojiReaction>>>
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ interface TimelineCases {
|
||||
fun delete(id: String): Single<DeletedStatus>
|
||||
fun pin(status: Status, pin: Boolean)
|
||||
fun voteInPoll(status: Status, choices: List<Int>): Single<Poll>
|
||||
|
||||
fun react(emoji: String, id: String, react: Boolean) : Single<Status>
|
||||
}
|
||||
|
||||
class TimelineCasesImpl(
|
||||
@ -157,4 +157,14 @@ class TimelineCasesImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override fun react(emoji: String, id: String, react: Boolean): Single<Status> {
|
||||
val call = if (react) {
|
||||
mastodonApi.reactWithEmoji(id, emoji)
|
||||
} else {
|
||||
mastodonApi.unreactWithEmoji(id, emoji)
|
||||
}
|
||||
return call.doAfterSuccess { status ->
|
||||
eventHub.dispatch(EmojiReactEvent(status))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.keylesspalace.tusky.util
|
||||
|
||||
enum class CardViewMode {
|
||||
NONE,
|
||||
FULL_WIDTH,
|
||||
INDENTED
|
||||
}
|
@ -82,6 +82,8 @@ class ListStatusAccessibilityDelegate(
|
||||
}
|
||||
if (status.reblogsCount > 0) info.addAction(openRebloggedByAction)
|
||||
if (status.favouritesCount > 0) info.addAction(openFavsAction)
|
||||
|
||||
info.addAction(moreAction)
|
||||
}
|
||||
|
||||
}
|
||||
@ -150,6 +152,9 @@ class ListStatusAccessibilityDelegate(
|
||||
interrupt()
|
||||
statusActionListener.onShowFavs(pos)
|
||||
}
|
||||
R.id.action_more -> {
|
||||
statusActionListener.onMore(host, pos)
|
||||
}
|
||||
else -> return super.performAccessibilityAction(host, action, args)
|
||||
}
|
||||
return true
|
||||
@ -311,5 +316,10 @@ class ListStatusAccessibilityDelegate(
|
||||
R.id.action_open_faved_by,
|
||||
context.getString(R.string.action_open_faved_by))
|
||||
|
||||
private val moreAction = AccessibilityActionCompat(
|
||||
R.id.action_more,
|
||||
context.getString(R.string.action_more)
|
||||
)
|
||||
|
||||
private data class LinkSpanInfo(val text: String, val link: String)
|
||||
}
|
@ -10,5 +10,9 @@ data class StatusDisplayOptions(
|
||||
@get:JvmName("showBotOverlay")
|
||||
val showBotOverlay: Boolean,
|
||||
@get:JvmName("useBlurhash")
|
||||
val useBlurhash: Boolean
|
||||
val useBlurhash: Boolean,
|
||||
@get:JvmName("cardViewMode")
|
||||
val cardViewMode: CardViewMode,
|
||||
@get:JvmName("confirmReblogs")
|
||||
val confirmReblogs: Boolean
|
||||
)
|
@ -10,7 +10,7 @@
|
||||
android:id="@+id/status_emoji_reaction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="5dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:minWidth="0dp"
|
||||
android:lines="1"
|
||||
|
@ -171,6 +171,73 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/status_content_warning_button"
|
||||
tools:text="This is a status" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/status_card_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/card_frame"
|
||||
android:clipChildren="true"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:minHeight="80dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@+id/status_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/card_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:layout_margin="1dp"
|
||||
android:background="?attr/colorBackgroundAccent"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="center" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/card_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:lines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/card_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:ellipsize="end"
|
||||
android:lineSpacingMultiplier="1.1"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/card_link"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textColor="?android:textColorTertiary"
|
||||
android:textSize="?attr/status_text_medium" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_toggle_content"
|
||||
style="@style/TuskyButton.Outlined"
|
||||
@ -189,7 +256,7 @@
|
||||
android:textSize="?attr/status_text_medium"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_card_view"
|
||||
tools:text="@string/status_content_show_less"
|
||||
tools:visibility="visible" />
|
||||
|
||||
@ -247,6 +314,18 @@
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
|
||||
tools:text="7 votes • 7 hours remaining" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/status_emoji_reactions"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="1dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_poll_description" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/status_reply"
|
||||
@ -262,7 +341,7 @@
|
||||
app:layout_constraintEnd_toStartOf="@id/status_inset"
|
||||
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||
app:layout_constraintStart_toStartOf="@id/status_display_name"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_emoji_reactions"
|
||||
app:srcCompat="@drawable/ic_reply_24dp" />
|
||||
|
||||
<at.connyduck.sparkbutton.SparkButton
|
||||
|
@ -131,7 +131,7 @@
|
||||
tools:text="Status content. Can be pretty long. " />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card_view"
|
||||
android:id="@+id/status_card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
@ -204,7 +204,7 @@
|
||||
android:layout_marginBottom="4dp"
|
||||
android:background="@drawable/media_preview_outline"
|
||||
android:importantForAccessibility="noHideDescendants"
|
||||
app:layout_constraintTop_toBottomOf="@id/card_view">
|
||||
app:layout_constraintTop_toBottomOf="@id/status_card_view">
|
||||
|
||||
<include layout="@layout/item_media_preview" />
|
||||
|
||||
|
13
app/src/main/res/menu/emoji_reaction_more.xml
Normal file
13
app/src/main/res/menu/emoji_reaction_more.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/emoji_react"
|
||||
android:title="@string/action_emoji_react" />
|
||||
<item
|
||||
android:id="@+id/emoji_unreact"
|
||||
android:title="@string/action_emoji_unreact" />
|
||||
<item
|
||||
android:id="@+id/emoji_reacted_by"
|
||||
android:title="@string/action_emoji_reacted_by" />
|
||||
</menu>
|
||||
|
@ -501,4 +501,6 @@
|
||||
<string name="no_scheduled_status">ليس لديك أية منشورات مُبرمَجة للنشر.</string>
|
||||
|
||||
<string name="error_audio_upload_size">يجب أن يكون حجم الملفات الصوتية أقل مِن 40 ميغابايت.</string>
|
||||
</resources>
|
||||
<string name="warning_scheduling_interval">تُقدّر أدنى فترة لبرمجة النشر في ماستدون بـ 5 دقائق.</string>
|
||||
|
||||
</resources>
|
||||
|
2
app/src/main/res/values-en-rAU/strings.xml
Normal file
2
app/src/main/res/values-en-rAU/strings.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
@ -469,4 +469,9 @@
|
||||
<string name="list">Listo</string>
|
||||
<string name="post_lookup_error_format">Eraro dum elserĉo de la mesaĝo %s</string>
|
||||
|
||||
</resources>
|
||||
<string name="error_audio_upload_size">Aŭdia dosiero devas esti malpli ol 40MB.</string>
|
||||
<string name="gradient_for_media">Montri buntajn transirojn por kaŝitaj aŭdovidaĵoj</string>
|
||||
|
||||
<string name="no_saved_status">Vi ne havas iun ajn malneton.</string>
|
||||
<string name="no_scheduled_status">Vi ne havas iun ajn planitan mesaĝon.</string>
|
||||
</resources>
|
||||
|
@ -39,7 +39,7 @@
|
||||
<string name="title_saved_toot">Brouillons</string>
|
||||
<string name="title_licenses">Licences</string>
|
||||
<string name="status_username_format">\@%s</string>
|
||||
<string name="status_boosted_format">%s a boosté</string>
|
||||
<string name="status_boosted_format">%s a partagé</string>
|
||||
<string name="status_sensitive_media_title">Contenu sensible</string>
|
||||
<string name="status_media_hidden_title">Média caché</string>
|
||||
<string name="status_sensitive_media_directions">Cliquer pour voir</string>
|
||||
@ -49,15 +49,15 @@
|
||||
<string name="status_content_show_less">Replier</string>
|
||||
<string name="message_empty">Rien ici.</string>
|
||||
<string name="footer_empty">Il n’y a aucun pouet pour le moment.\nGlissez vers le bas pour actualiser !</string>
|
||||
<string name="notification_reblog_format">%s a boosté votre pouet</string>
|
||||
<string name="notification_reblog_format">%s a partagé votre pouet</string>
|
||||
<string name="notification_favourite_format">%s a ajouté votre pouet à ses favoris</string>
|
||||
<string name="notification_follow_format">%s vous suit</string>
|
||||
<string name="report_username_format">Signaler @%s</string>
|
||||
<string name="report_comment_hint">Commentaires additonnels ?</string>
|
||||
<string name="action_quick_reply">Réponse rapide</string>
|
||||
<string name="action_reply">Répondre</string>
|
||||
<string name="action_reblog">Booster</string>
|
||||
<string name="action_unreblog">Supprimer le boost</string>
|
||||
<string name="action_reblog">Partager</string>
|
||||
<string name="action_unreblog">Annuler le partage</string>
|
||||
<string name="action_favourite">Favori</string>
|
||||
<string name="action_unfavourite">Supprimer le favori</string>
|
||||
<string name="action_more">Plus</string>
|
||||
@ -69,8 +69,8 @@
|
||||
<string name="action_unfollow">Ne plus suivre</string>
|
||||
<string name="action_block">Bloquer</string>
|
||||
<string name="action_unblock">Débloquer</string>
|
||||
<string name="action_hide_reblogs">Cacher les boosts</string>
|
||||
<string name="action_show_reblogs">Montrer les boosts</string>
|
||||
<string name="action_hide_reblogs">Cacher les partages</string>
|
||||
<string name="action_show_reblogs">Montrer les partages</string>
|
||||
<string name="action_report">Signaler</string>
|
||||
<string name="action_delete">Supprimer</string>
|
||||
<string name="action_send">POUET</string>
|
||||
@ -109,8 +109,8 @@
|
||||
<string name="action_links">Liens</string>
|
||||
<string name="action_mentions">Mentions</string>
|
||||
<string name="action_hashtags">Hashtags</string>
|
||||
<string name="action_open_reblogger">Afficher l’auteur·rice du boost</string>
|
||||
<string name="action_open_reblogged_by">Afficher les boosts</string>
|
||||
<string name="action_open_reblogger">Afficher l’auteur·rice du partage</string>
|
||||
<string name="action_open_reblogged_by">Montrer les partages</string>
|
||||
<string name="action_open_faved_by">Montrer les favoris</string>
|
||||
<string name="title_hashtags_dialog">Hashtags</string>
|
||||
<string name="title_mentions_dialog">Mentions</string>
|
||||
@ -170,7 +170,7 @@
|
||||
<string name="pref_title_notification_filters">Me notifier lorsque</string>
|
||||
<string name="pref_title_notification_filter_mentions">on me mentionne</string>
|
||||
<string name="pref_title_notification_filter_follows">on me suit</string>
|
||||
<string name="pref_title_notification_filter_reblogs">mes messages sont boostés</string>
|
||||
<string name="pref_title_notification_filter_reblogs">mes pouets sont partagés</string>
|
||||
<string name="pref_title_notification_filter_favourites">mes messages sont mis en favoris</string>
|
||||
<string name="pref_title_appearance_settings">Apparence</string>
|
||||
<string name="pref_title_app_theme">Thème de l’application</string>
|
||||
@ -187,7 +187,7 @@
|
||||
<string name="pref_title_language">Langue</string>
|
||||
<string name="pref_title_status_filter">Filtrage des fils</string>
|
||||
<string name="pref_title_status_tabs">Onglets</string>
|
||||
<string name="pref_title_show_boosts">Afficher les boosts</string>
|
||||
<string name="pref_title_show_boosts">Montrer les partages</string>
|
||||
<string name="pref_title_show_replies">Afficher les réponses</string>
|
||||
<string name="pref_title_show_media_preview">Montrer les miniatures des médias</string>
|
||||
<string name="pref_title_proxy_settings">Proxy</string>
|
||||
@ -213,7 +213,7 @@
|
||||
<string name="notification_follow_name">Nouveaux abonnés</string>
|
||||
<string name="notification_follow_description">Notifications pour les nouveaux abonnés</string>
|
||||
<string name="notification_boost_name">Partages</string>
|
||||
<string name="notification_boost_description">Notifications quand vos pouets sont boostés</string>
|
||||
<string name="notification_boost_description">Notifications quand vos pouets sont partagés</string>
|
||||
<string name="notification_favourite_name">Favoris</string>
|
||||
<string name="notification_favourite_description">Notifications quand vos pouets sont mis en favoris</string>
|
||||
<string name="notification_mention_format">%s vous a mentionné</string>
|
||||
@ -316,8 +316,8 @@
|
||||
<string name="download_failed">Échec du téléchargement</string>
|
||||
<string name="profile_badge_bot_text">Robot</string>
|
||||
<string name="account_moved_description">%1$s a déménagé vers :</string>
|
||||
<string name="reblog_private">Booster vers l’audience originale</string>
|
||||
<string name="unreblog_private">Ne plus booster</string>
|
||||
<string name="reblog_private">Partager à l’audience originale</string>
|
||||
<string name="unreblog_private">Annuler le partage</string>
|
||||
<string name="license_description">Tusky contient du code et des ressources issus des projets open source suivants :</string>
|
||||
<string name="license_apache_2">Sous licence Apache (copie ci-dessous)</string>
|
||||
<string name="license_cc_by_4">CC-BY 4.0</string>
|
||||
@ -335,10 +335,10 @@
|
||||
<item quantity="other"><b>%1$s</b> Favoris</item>
|
||||
</plurals>
|
||||
<plurals name="reblogs">
|
||||
<item quantity="one"><b>%s</b> Boost</item>
|
||||
<item quantity="other"><b>%s</b> Boosts</item>
|
||||
</plurals>
|
||||
<string name="title_reblogged_by">Boosté par</string>
|
||||
<item quantity="one"><b>%s</b> Partage</item>
|
||||
<item quantity="other"><b>%s</b> Partages</item>
|
||||
</plurals>
|
||||
<string name="title_reblogged_by">Partagé par</string>
|
||||
<string name="title_favourited_by">Mis en favoris par</string>
|
||||
<string name="conversation_1_recipients">%1$s</string>
|
||||
<string name="conversation_2_recipients">%1$s et %2$s</string>
|
||||
@ -371,7 +371,7 @@
|
||||
<string name="notifications_apply_filter">Filtrer</string>
|
||||
<string name="filter_apply">Appliquer</string>
|
||||
|
||||
<string name="compose_shortcut_long_label">Écrire un pouet</string>
|
||||
<string name="compose_shortcut_long_label">Rédiger un pouet</string>
|
||||
<string name="compose_shortcut_short_label">Écrire</string>
|
||||
<string name="pref_title_bot_overlay">Afficher l\'indicateur de robots</string>
|
||||
|
||||
@ -486,4 +486,6 @@
|
||||
<string name="no_saved_status">Vous n’avez aucun brouillon.</string>
|
||||
<string name="no_scheduled_status">Vous n’avez aucun pouet planifié.</string>
|
||||
|
||||
<string name="warning_scheduling_interval">L’intervalle minimum de planification sur Mastodon est de 5 minutes.</string>
|
||||
|
||||
</resources>
|
||||
|
@ -508,4 +508,6 @@
|
||||
<string name="no_scheduled_status">Þú ert ekki með neinar áætlaðar stöðufærslur.</string>
|
||||
|
||||
<string name="error_audio_upload_size">Hljóðskrár verða að vera minni en 40MB.</string>
|
||||
</resources>
|
||||
<string name="warning_scheduling_interval">Mastodon er með 5 mínútna lágmarksbil fyrir áætlaðar aðgerðir.</string>
|
||||
|
||||
</resources>
|
||||
|
@ -409,4 +409,10 @@
|
||||
<string name="pref_title_show_notifications_filter">通知フィルターを表示</string>
|
||||
|
||||
|
||||
<string name="action_reset_schedule">リセット</string>
|
||||
<string name="error_audio_upload_size">音声ファイルは40MB未満にしてください。</string>
|
||||
<string name="title_bookmarks">ブックマーク</string>
|
||||
<string name="action_bookmark">ブックマーク</string>
|
||||
<string name="action_edit">編集</string>
|
||||
<string name="action_view_bookmarks">ブックマーク</string>
|
||||
</resources>
|
||||
|
@ -1,22 +1,22 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources><string name="action_login">Qqen γer Maṣṭudun</string>
|
||||
<resources><string name="action_login">Qqen ɣer Maṣṭudun</string>
|
||||
<string name="title_favourites">Ismenyifen</string>
|
||||
<string name="title_saved_toot">Irewwayen</string>
|
||||
<string name="action_logout">Ffeγ</string>
|
||||
<string name="action_view_preferences">Iγewwaṛen</string>
|
||||
<string name="action_view_account_preferences">Iγewwaṛen n umiḍan</string>
|
||||
<string name="action_edit_profile">Ẓreg amaγnu</string>
|
||||
<string name="action_logout">Ffeɣ</string>
|
||||
<string name="action_view_preferences">Iɣewwaṛen</string>
|
||||
<string name="action_view_account_preferences">Iɣewwaṛen n umiḍan</string>
|
||||
<string name="action_edit_profile">Ẓreg amaɣnu</string>
|
||||
<string name="action_search">Nadi</string>
|
||||
<string name="about_title_activity">Γef</string>
|
||||
<string name="action_lists">Umuγen</string>
|
||||
<string name="title_lists">Umuγen</string>
|
||||
<string name="error_compose_character_limit">Tijewwiqt-ik aṭas i γuzzifet!</string>
|
||||
<string name="about_title_activity">Ɣef</string>
|
||||
<string name="action_lists">Umuɣen</string>
|
||||
<string name="title_lists">Umuɣen</string>
|
||||
<string name="error_compose_character_limit">Tijewwiqt-ik aṭas i ɣuzzifet!</string>
|
||||
<string name="title_home">Agejdan</string>
|
||||
<string name="title_tab_preferences">Iccaren</string>
|
||||
<string name="title_view_thread">Tijewwiqt</string>
|
||||
<string name="title_statuses">Iznan</string>
|
||||
<string name="title_statuses_with_replies">S tririyin</string>
|
||||
<string name="title_edit_profile">Ẓreg amaγnu-ik</string>
|
||||
<string name="title_edit_profile">Ẓreg amaɣnu-ik</string>
|
||||
<string name="status_username_format">\@%s</string>
|
||||
<string name="status_content_warning_show_more">Zeṛ ugar</string>
|
||||
<string name="status_content_warning_show_less">Zeṛ kra kan</string>
|
||||
@ -35,13 +35,13 @@
|
||||
<string name="action_send_public">JEWWEQ!</string>
|
||||
<string name="action_retry">Ɛreḍ tikkelt-nniḍen</string>
|
||||
<string name="action_close">Derreɛ</string>
|
||||
<string name="action_view_profile">Amaγnu</string>
|
||||
<string name="action_view_profile">Amaɣnu</string>
|
||||
<string name="action_view_favourites">Ismenyifen</string>
|
||||
<string name="action_open_in_web">Ldi deg uminig</string>
|
||||
<string name="action_share">Bḍu</string>
|
||||
<string name="action_mute">Sgugem</string>
|
||||
<string name="action_access_saved_toot">Irewwayen</string>
|
||||
<string name="action_open_faved_by">Sken-ed ismenyifen</string>
|
||||
<string name="action_open_faved_by">Sken-d ismenyifen</string>
|
||||
|
||||
<string name="notification_favourite_name">Ismenyifen</string>
|
||||
<plurals name="favs">
|
||||
@ -51,11 +51,11 @@
|
||||
|
||||
<string name="no_saved_status">Ur tesɛiḍ ara irewwayen.</string>
|
||||
<string name="error_generic">Tella-d tucḍa.</string>
|
||||
<string name="title_notifications">Tilγa</string>
|
||||
<string name="title_notifications">Tilɣa</string>
|
||||
<string name="link_whats_an_instance">D acu i ttummant\?</string>
|
||||
|
||||
<string name="title_bookmarks">Ticraḍ</string>
|
||||
<string name="action_bookmark">Rnu γer ticraḍ</string>
|
||||
<string name="action_bookmark">Rnu ɣer ticraḍ</string>
|
||||
<string name="action_view_bookmarks">Ticraḍ</string>
|
||||
<string name="action_mute_domain">Sgugem %s</string>
|
||||
<string name="action_mention">Bder</string>
|
||||
@ -64,20 +64,20 @@
|
||||
<string name="action_undo">Sefsex</string>
|
||||
<string name="action_emoji_keyboard">Anasiw n imujiyen</string>
|
||||
<string name="action_add_tab">Rnu iccer</string>
|
||||
<string name="action_copy_link">Nγel aseγwen</string>
|
||||
<string name="action_copy_link">Nɣel aseɣwen</string>
|
||||
<string name="action_open_as">Ldi amzun d %s</string>
|
||||
<string name="action_share_as">Bḍu amzun d…</string>
|
||||
<string name="send_status_link_to">Bḍu aseγwen n tijewwiq s…</string>
|
||||
<string name="send_status_link_to">Bḍu aseɣwen n tijewwiq s…</string>
|
||||
<string name="send_status_content_to">Bḍu tijewwiqt d…</string>
|
||||
<string name="hint_domain">Anta tummant\?</string>
|
||||
<string name="hint_compose">d-acu i gellan d amaynut\?</string>
|
||||
<string name="hint_search">Nadi…</string>
|
||||
|
||||
<string name="label_quick_reply">Tiririn…</string>
|
||||
<string name="label_avatar">Tugna n umaγnu</string>
|
||||
<string name="label_avatar">Tugna n umaɣnu</string>
|
||||
<string name="dialog_download_image">Sider</string>
|
||||
<string name="dialog_delete_toot_warning">Kkes tijewwiqt-a\?</string>
|
||||
<string name="pref_title_edit_notification_settings">Ẓreg tilγa</string>
|
||||
<string name="pref_title_edit_notification_settings">Ẓreg tilɣa</string>
|
||||
<string name="pref_title_appearance_settings">Agrudem</string>
|
||||
<string name="app_theme_light">Aceɛlal</string>
|
||||
<string name="app_theme_black">Aberkan</string>
|
||||
@ -88,7 +88,7 @@
|
||||
<string name="notification_summary_medium">%1$s, %2$s, akked %3$s</string>
|
||||
<string name="notification_summary_small">%1$s akked %2$s</string>
|
||||
<string name="about_tusky_version">Tusky %s</string>
|
||||
<string name="about_tusky_account">Amaγnu n Tusky</string>
|
||||
<string name="about_tusky_account">Amaɣnu n Tusky</string>
|
||||
|
||||
<string name="status_media_images">Tugniwin</string>
|
||||
<string name="status_media_video">Tibidyutin</string>
|
||||
@ -110,16 +110,16 @@
|
||||
|
||||
<string name="action_view_mutes">Imiḍanen yettwasgugmen</string>
|
||||
<string name="action_view_blocks">Imiḍanen yettusḥebsen</string>
|
||||
<string name="action_view_domain_mutes">Tiγula yettwaffren</string>
|
||||
<string name="action_view_domain_mutes">Tiɣula yettwaffren</string>
|
||||
<string name="action_view_follow_requests">Isuturen n teḍfeṛt</string>
|
||||
<string name="action_view_media">Taγwalt</string>
|
||||
<string name="action_view_media">Taɣwalt</string>
|
||||
<string name="notifications_clear">Sfeḍ</string>
|
||||
<string name="title_mutes">Imiḍanen yettwasgugmen</string>
|
||||
<string name="title_blocks">Imiḍanen yettusḥebsen</string>
|
||||
<string name="title_domain_mutes">Tiγula yettwaffren</string>
|
||||
<string name="title_domain_mutes">Tiɣula yettwaffren</string>
|
||||
<string name="title_follow_requests">Isuturen n teḍfeṛt</string>
|
||||
<string name="pref_title_notifications_enabled">Ẓreg tilγa</string>
|
||||
<string name="title_media">Taγwalt</string>
|
||||
<string name="pref_title_notifications_enabled">Tilɣa</string>
|
||||
<string name="title_media">Taywalt</string>
|
||||
<string name="action_remove">Kkes</string>
|
||||
<string name="compose_shortcut_short_label">Azen</string>
|
||||
|
||||
@ -129,27 +129,27 @@
|
||||
<string name="action_add_poll">Rnu assenqed</string>
|
||||
<string name="action_photo_take">Ṭef tugna</string>
|
||||
<string name="action_toggle_visibility">Timeẓriwt n tijewwaqt</string>
|
||||
<string name="action_schedule_toot">Sγiwes tijewwaqt-a</string>
|
||||
<string name="action_schedule_toot">Sɣiwes tijewwaqt-a</string>
|
||||
<string name="status_share_content">Bḍu agbur n tijewwiqt-a</string>
|
||||
<string name="status_share_link">Bḍu aseγwen γer tijewwiqt</string>
|
||||
<string name="status_share_link">Bḍu aseɣwen ɣer tijewwiqt</string>
|
||||
<string name="filter_addition_dialog_title">Rnu amsizdeg</string>
|
||||
<string name="filter_edit_dialog_title">Ẓreg amsizdeg</string>
|
||||
<string name="action_create_list">Snulfu-d umuγ</string>
|
||||
<string name="action_rename_list">Snifel isem n wumuγ</string>
|
||||
<string name="action_delete_list">Kkes umuγ-a</string>
|
||||
<string name="action_edit_list">Ẓreg umuγ-a</string>
|
||||
<string name="action_add_to_list">Rnu yiwen umiḍan γer tabdert</string>
|
||||
<string name="action_remove_from_list">Kkes amiḍan seg wumuγ</string>
|
||||
<string name="action_create_list">Snulfu-d umuɣ</string>
|
||||
<string name="action_rename_list">Snifel isem n wumuɣ</string>
|
||||
<string name="action_delete_list">Kkes umuɣ-a</string>
|
||||
<string name="action_edit_list">Ẓreg umuɣ-a</string>
|
||||
<string name="action_add_to_list">Rnu yiwen umiḍan ɣer wummuɣ</string>
|
||||
<string name="action_remove_from_list">Kkes amiḍan seg wumuɣ</string>
|
||||
|
||||
<string name="profile_metadata_add">Rnu isefka</string>
|
||||
<string name="hint_list_name">Isem n wumuγ</string>
|
||||
<string name="hint_list_name">Isem n wumuɣ</string>
|
||||
|
||||
<string name="select_list_title">Fren tabdart</string>
|
||||
<string name="list">Umuγ</string>
|
||||
<string name="list">Umuɣ</string>
|
||||
<string name="notifications_apply_filter">Sizdeg</string>
|
||||
<string name="title_accounts">Imiḍanen</string>
|
||||
<string name="add_poll_choice">Rnu yiwen wefran</string>
|
||||
<string name="report_username_format">Ccetki γef @%s</string>
|
||||
<string name="report_username_format">Ccetki ɣef @%s</string>
|
||||
<string name="action_report">Ccetki</string>
|
||||
<string name="action_reject">Ggami</string>
|
||||
<string name="download_image">Yessidired %1$s</string>
|
||||
@ -159,16 +159,16 @@
|
||||
<string name="login_connection">itteqqen…</string>
|
||||
|
||||
<string name="dialog_message_uploading_media">Issalay…</string>
|
||||
<string name="pref_title_notification_filter_poll">fukken kran n wadγaren</string>
|
||||
<string name="pref_title_notification_filter_poll">fukken kran n wadɣaren</string>
|
||||
<string name="pref_title_timeline_filters">Imzizdigen</string>
|
||||
|
||||
<string name="app_theme_auto">Akken yella yiṭij</string>
|
||||
<string name="pref_title_browser_settings">Iminig</string>
|
||||
<string name="pref_title_show_replies">Sken-ed tiririyin</string>
|
||||
<string name="pref_title_show_replies">Sken-d tiririyin</string>
|
||||
<string name="pref_title_http_proxy_settings">Apṛuksi HTTP</string>
|
||||
<string name="pref_title_http_proxy_server">Tansa n upṛuksi HTTP</string>
|
||||
<string name="notification_follow_name">Imeḍfaṛen imaynuten</string>
|
||||
<string name="notification_poll_name">Adγaren</string>
|
||||
<string name="notification_poll_name">Adɣaren</string>
|
||||
<string name="notification_mention_format">Yuder-ik-id %s</string>
|
||||
<string name="description_account_locked">Yettwargel umiḍan</string>
|
||||
|
||||
@ -183,26 +183,26 @@
|
||||
<string name="restart">Ales tanekra</string>
|
||||
<string name="download_failed">Tuccḍa n usider</string>
|
||||
|
||||
<string name="account_moved_description">Igujj %1$s γer:</string>
|
||||
<string name="account_moved_description">Igujj %1$s ɣer:</string>
|
||||
|
||||
<string name="unpin_action">Kkes asenṭeḍ</string>
|
||||
<string name="conversation_1_recipients">%1$s</string>
|
||||
<string name="conversation_2_recipients">%1$s akked %2$s</string>
|
||||
<string name="conversation_more_recipients">%1$s, %2$s akked %3$d nniḍen</string>
|
||||
<string name="compose_shortcut_long_label">Aru tijewwiqt</string>
|
||||
<string name="poll_info_format"> <!-- 15 n wadγaren • 1 n wesrag id yeqqimen --> %1$s • %2$s</string>
|
||||
<string name="poll_info_format"> <!-- 15 n wadɣaren • 1 n wesrag id yeqqimen --> %1$s • %2$s</string>
|
||||
<plurals name="poll_info_votes">
|
||||
<item quantity="one">%s wedγar</item>
|
||||
<item quantity="other">%s n yedγaren</item>
|
||||
<item quantity="one">%s n wedɣar</item>
|
||||
<item quantity="other">%s n yedɣaren</item>
|
||||
</plurals>
|
||||
<string name="poll_info_time_relative">%s id yugran</string>
|
||||
<string name="poll_info_time_absolute">ad ifak deg %s</string>
|
||||
<string name="poll_info_closed">ifuk</string>
|
||||
|
||||
<string name="poll_vote">Dγer</string>
|
||||
<string name="poll_vote">Dɣer</string>
|
||||
|
||||
<string name="poll_ended_voted">Ifuk, tura kan, yiwen wedγar t tteki-iḍ degs</string>
|
||||
<string name="poll_ended_created">Ifukk yiwen wedγar id snulfaḍ</string>
|
||||
<string name="poll_ended_voted">Ifuk, tura kan, yiwen wedɣar t tteki-iḍ degs</string>
|
||||
<string name="poll_ended_created">Ifukk yiwen wedɣar id snulfaḍ</string>
|
||||
|
||||
<plurals name="poll_timespan_days">
|
||||
<item quantity="one">%d n wass</item>
|
||||
@ -214,18 +214,18 @@
|
||||
</plurals>
|
||||
<plurals name="poll_timespan_minutes">
|
||||
<item quantity="one">%d n tasdidt</item>
|
||||
<item quantity="other">%d n tesdidin</item>
|
||||
<item quantity="other">%d n tisdidin</item>
|
||||
</plurals>
|
||||
<string name="button_continue">Kemmel</string>
|
||||
<string name="button_back">Uγal</string>
|
||||
<string name="button_back">Uɣal</string>
|
||||
<string name="failed_report">Tella-d tuccḍa deg ccetki</string>
|
||||
<string name="failed_search">Tucḍa n unadi</string>
|
||||
|
||||
<string name="create_poll_title">Assenqed</string>
|
||||
<string name="poll_duration_5_min">5 n tasditin</string>
|
||||
<string name="poll_duration_30_min">30 n tasditin</string>
|
||||
<string name="poll_duration_1_hour">1 n wesrag</string>
|
||||
<string name="poll_duration_6_hours">6 n wesragen</string>
|
||||
<string name="poll_duration_5_min">5 n tisdidin</string>
|
||||
<string name="poll_duration_30_min">30 n tisdidin</string>
|
||||
<string name="poll_duration_1_hour">1 n usrag</string>
|
||||
<string name="poll_duration_6_hours">6 n isragen</string>
|
||||
<string name="poll_duration_1_day">1 n wass</string>
|
||||
<string name="poll_duration_3_days">3 n wussan</string>
|
||||
<string name="poll_duration_7_days">7 n wussan</string>
|
||||
@ -233,30 +233,66 @@
|
||||
|
||||
<string name="title_follows">Ig ṭafaṛ</string>
|
||||
<string name="title_followers">Imeḍfaṛen</string>
|
||||
<string name="hint_search_people_list">Nadi γef medden i teṭafareḍ</string>
|
||||
<string name="hint_search_people_list">Nadi ɣef medden i teṭafareḍ</string>
|
||||
<string name="description_visiblity_private">Imeḍfaṛen</string>
|
||||
|
||||
<string name="action_links">Iseγwan</string>
|
||||
<string name="action_links">Iseɣwan</string>
|
||||
<string name="action_mentions">Tibdarin</string>
|
||||
<string name="title_mentions_dialog">Tibdarin</string>
|
||||
<string name="title_links_dialog">Iseγwan</string>
|
||||
<string name="title_links_dialog">Iseɣwan</string>
|
||||
<string name="confirmation_reported">Yettwaceyyaɛ!</string>
|
||||
<string name="status_sent">Yettwaceyyaɛ!</string>
|
||||
<string name="search_no_results">Ula d yiwen n ugmuḍ</string>
|
||||
|
||||
<string name="post_privacy_followers_only">I yimeḍfaṛen kan</string>
|
||||
|
||||
<string name="pref_status_text_size">Teγzi n weḍṛis</string>
|
||||
<string name="pref_status_text_size">Teɣzi n weḍṛis</string>
|
||||
|
||||
<string name="about_powered_by_tusky">Yettwamdemmar s Tusky</string>
|
||||
<string name="about_project_site">Asmel Web n usenfaṛ:
|
||||
\n https://tusky.app</string>
|
||||
<string name="abbreviated_hours_ago">%dasr</string>
|
||||
<string name="abbreviated_minutes_ago">%dtas</string>
|
||||
<string name="abbreviated_seconds_ago">%dtasn</string>
|
||||
<string name="abbreviated_hours_ago">%dsr</string>
|
||||
<string name="abbreviated_minutes_ago">%dtsd</string>
|
||||
<string name="abbreviated_seconds_ago">%dtsn</string>
|
||||
|
||||
<string name="compose_save_draft">Sekles amzun d arewway\?</string>
|
||||
<string name="later">Ticki</string>
|
||||
<string name="profile_badge_bot_text">Aṛubut</string>
|
||||
<string name="description_status_bookmarked">Yettwarna γer ticṛad</string>
|
||||
<string name="description_status_bookmarked">Yettwarna ɣer ticṛad</string>
|
||||
<string name="abbreviated_in_hours">deg %dsr</string>
|
||||
<string name="abbreviated_in_minutes">deg %dtsd</string>
|
||||
<string name="abbreviated_in_seconds">deg %dtsn</string>
|
||||
<plurals name="poll_timespan_seconds">
|
||||
<item quantity="one">%d n tasint</item>
|
||||
<item quantity="other">%d n tasinin</item>
|
||||
</plurals>
|
||||
|
||||
<string name="status_sensitive_media_title">Agbur amḥulfu</string>
|
||||
<string name="pref_default_media_sensitivity">Creḍ allal n teywalt amzun d amḥulfu</string>
|
||||
<string name="action_reset_schedule">Wennez tikkelt-nniḍen</string>
|
||||
<string name="error_media_upload_sending">Asali ur yeddi ara.</string>
|
||||
<string name="error_sender_account_gone">Tuccḍa deg tuzna n tijewwiqt.</string>
|
||||
|
||||
<string name="title_public_local">Adigan</string>
|
||||
<string name="title_licenses">Turagin</string>
|
||||
|
||||
<string name="status_boosted_format">Yebḍa-t %s</string>
|
||||
<string name="notification_reblog_format">Yebḍa %s tijewwiqt-ik·im</string>
|
||||
<string name="notification_favourite_format">Yerna %s tijewwiqt-ik·im ɣer imenyafen-is</string>
|
||||
<string name="action_quick_reply">Tiririt taruradt</string>
|
||||
<string name="action_reblog">Bḍu</string>
|
||||
<string name="action_unreblog">Kkes beṭu</string>
|
||||
<string name="action_hide_reblogs">Ffer beṭuyat</string>
|
||||
<string name="action_show_reblogs">Sken-d beṭuyat</string>
|
||||
<string name="action_unmute">Ur sgugum ara</string>
|
||||
<string name="action_accept">Ddeg</string>
|
||||
<string name="action_hashtags">Ihacṭagen</string>
|
||||
<string name="action_open_reblogged_by">Sken-d beṭuyat</string>
|
||||
<string name="title_hashtags_dialog">Ihacṭagen</string>
|
||||
<string name="confirmation_unmuted">Aseqdac nni ur yettwasgugem ara tura</string>
|
||||
<string name="confirmation_domain_unmuted">%s ur yettwaffer ara</string>
|
||||
|
||||
<string name="hint_note">Assisen</string>
|
||||
<string name="label_header">Tugna n yiɣef n umaɣnu</string>
|
||||
|
||||
</resources>
|
||||
|
@ -520,4 +520,6 @@
|
||||
|
||||
<string name="no_saved_status">Du har ikke lagret noen kladder.</string>
|
||||
<string name="error_audio_upload_size">Lydfiler må være mindre enn 40MB.</string>
|
||||
</resources>
|
||||
<string name="warning_scheduling_interval">Mastodon har et minimums planleggingsinterval på 5 minutter.</string>
|
||||
|
||||
</resources>
|
||||
|
@ -498,4 +498,6 @@
|
||||
<string name="no_saved_status">Nie masz żadnych szkiców.</string>
|
||||
<string name="no_scheduled_status">Nie masz żadnych zaplanowanych wpisów.</string>
|
||||
|
||||
<string name="warning_scheduling_interval">Mastodon umożliwia wysłanie minimalnie 5 minut od zaplanowania.</string>
|
||||
|
||||
</resources>
|
||||
|
@ -486,4 +486,5 @@
|
||||
|
||||
<string name="error_audio_upload_size">Áudios devem ser menores que 40MB.</string>
|
||||
<string name="no_saved_status">Sem rascunhos.</string>
|
||||
<string name="warning_scheduling_interval">Mastodon possui um intervalo mínimo de 5 minutos para agendar.</string>
|
||||
</resources>
|
||||
|
@ -546,4 +546,13 @@
|
||||
<string name="description_status_bookmarked">Добавлено в закладки</string>
|
||||
<string name="select_list_title">Выбрать список</string>
|
||||
<string name="list">Список</string>
|
||||
</resources>
|
||||
<string name="error_audio_upload_size">Аудиофайлы должны быть меньше 40МБ.</string>
|
||||
<string name="gradient_for_media">Показывать цветные градиенты для скрытых медиа</string>
|
||||
|
||||
<string name="post_lookup_error_format">Ошибка поиска поста %s</string>
|
||||
|
||||
<string name="no_saved_status">У вас нет черновиков.</string>
|
||||
<string name="no_scheduled_status">У вас нет запланированных постов.</string>
|
||||
<string name="warning_scheduling_interval">Минимальный интервал планирования в Mastodon составляет 5 минут.</string>
|
||||
|
||||
</resources>
|
||||
|
@ -482,4 +482,5 @@
|
||||
|
||||
<string name="error_audio_upload_size">Ljudfiler måste vara mindre än 40MB.</string>
|
||||
<string name="no_saved_status">Du har inga utkast.</string>
|
||||
<string name="warning_scheduling_interval">Mastodon har ett minimalt schemaläggningsintervall på 5 minuter.</string>
|
||||
</resources>
|
||||
|
@ -22,4 +22,5 @@
|
||||
<item name="action_open_reblogger" type="id" />
|
||||
<item name="action_open_reblogged_by" type="id" />
|
||||
<item name="action_open_faved_by" type="id" />
|
||||
<item name="action_more" type="id" />
|
||||
</resources>
|
@ -2,8 +2,13 @@
|
||||
|
||||
<string name="action_reply_to">Reply to</string>
|
||||
<string name="action_markdown">Markdown</string>
|
||||
<string name="action_mute_conversation">Mute conversation</string>
|
||||
<string name="action_unmute_conversation">Unmute conversation</string>
|
||||
<string name="action_mute_conversation">Mute conversation</string>
|
||||
<string name="action_unmute_conversation">Unmute conversation</string>
|
||||
<string name="action_emoji_react">Add reaction</string>
|
||||
<string name="action_emoji_unreact">Remove reaction</string>
|
||||
<string name="action_emoji_reacted_by">Who reacted</string>
|
||||
|
||||
<string name="title_emoji_reacted_by">%s reacted by</string>
|
||||
|
||||
<string name="hint_appname">Application name</string>
|
||||
<string name="hint_website">Application website</string>
|
||||
@ -13,7 +18,7 @@
|
||||
|
||||
<string name="error_media_upload_size">File size exceeds instance limits</string>
|
||||
|
||||
<string name="notification_emoji_format">%s reacted to your post with %s</string>
|
||||
<string name="notification_emoji_format">%s reacted with %s to your post</string>
|
||||
<string name="notification_emoji_name">Emoji Reactions</string>
|
||||
<string name="notification_emoji_description">Notifications about new emoji reactions</string>
|
||||
|
||||
|
@ -548,5 +548,7 @@
|
||||
<string name="no_saved_status">You don\'t have any drafts.</string>
|
||||
<string name="no_scheduled_status">You don\'t have any scheduled statuses.</string>
|
||||
<string name="warning_scheduling_interval">Mastodon has a minimum scheduling interval of 5 minutes.</string>
|
||||
<string name="pref_title_show_cards_in_timelines">Show link previews in timelines</string>
|
||||
<string name="pref_title_confirm_reblogs">Show confirmation dialog before boosting</string>
|
||||
|
||||
</resources>
|
||||
|
@ -72,6 +72,18 @@
|
||||
android:title="@string/pref_title_show_notifications_filter"
|
||||
app:singleLineTitle="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="showCardsInTimelines"
|
||||
android:title="@string/pref_title_show_cards_in_timelines"
|
||||
app:singleLineTitle="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="confirmReblogs"
|
||||
android:title="@string/pref_title_confirm_reblogs"
|
||||
app:singleLineTitle="false" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/pref_title_browser_settings">
|
||||
|
@ -5,7 +5,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.android.tools.build:gradle:3.6.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
3
fastlane/metadata/android/de/changelogs/68.txt
Normal file
3
fastlane/metadata/android/de/changelogs/68.txt
Normal file
@ -0,0 +1,3 @@
|
||||
Tusky v9.1
|
||||
|
||||
Dieses Release stellt die Kompatibilität mit Mastodon 3 sicher und verbessert Geschwindigkeit und Stabilität der App.
|
@ -5,4 +5,4 @@ Tusky v10.0
|
||||
- Du kannst jetzt Listen auf dem Hauptbildschirm anzeigen.
|
||||
- Du kannst jetzt Audio-Anhänge versenden.
|
||||
|
||||
Und viele andere Verbesserungen und Fehlerkorrekturen!
|
||||
Und viele andere Verbesserungen und Fehlerkorrekturen!
|
||||
|
8
fastlane/metadata/android/fa/changelogs/70.txt
Normal file
8
fastlane/metadata/android/fa/changelogs/70.txt
Normal file
@ -0,0 +1,8 @@
|
||||
تاسکی نگارش ۱۰.۰
|
||||
|
||||
- اکنون میتوانید بوقها را نشان کرده و نشانکهایتان را فهرست کنید.
|
||||
- اکنون میتوانید بوقها را زمانبندی کنید. توجّه داشته باشید که زمان گزیدهتان باید لااقل ۵ دقیقه بعد باشد.
|
||||
- اکنون میتوانید فهرستها را به صفحهٔ اصلی بیفزایید.
|
||||
- اکنون میتوانید پیوستهای صوتی بفرستید.
|
||||
|
||||
و بسیاری از بهبودها و رفع اشکالهای ریز!
|
8
fastlane/metadata/android/is/changelogs/70.txt
Normal file
8
fastlane/metadata/android/is/changelogs/70.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Tusky v10.0
|
||||
|
||||
- Þú getur núna bákamerkt stöðufærslur og gert lista með bókamerkjunum þínum í Tusky.
|
||||
- Þú getur núna sett tíst á áætlun í Tusky. Athugaðu að tíminn sem þú velur þarf að vera í það minnsta efti 5 mínútur.
|
||||
- Þú getur núna bætt listum á aðalskjáinn.
|
||||
- Þú getur núna sent in hljóðskrár sem viðhengi í Tusky.
|
||||
|
||||
Auk hellings af smærri lagfæringum og bætingum!
|
8
fastlane/metadata/android/ja/changelogs/70.txt
Normal file
8
fastlane/metadata/android/ja/changelogs/70.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Tusky v10.0
|
||||
|
||||
- Tuskyでブックマークの登録、表示が行えるようになりました。
|
||||
- You can now schedule toots with Tusky. Note that the time you select has to be at least 5 minutes in the future.
|
||||
- You can now add lists to the main screen.
|
||||
- Tuskyで音声ファイルを投稿出来るようになりました。
|
||||
|
||||
その他、多くの細かな改善とバグの修正を行いました!
|
12
fastlane/metadata/android/ja/full_description.txt
Normal file
12
fastlane/metadata/android/ja/full_description.txt
Normal file
@ -0,0 +1,12 @@
|
||||
Tuskyは、自由でオープンソースのソーシャルネットワークサーバーであるMastodon用の動作の高速なクライアントです。
|
||||
|
||||
• マテリアルデザイン
|
||||
• ほとんどのMastodon APIに対応
|
||||
• マルチアカウント対応
|
||||
• 時刻に基づいて自動変更可能なダークテーマとライトテーマ
|
||||
• 下書き - トゥートを作成し、保存しておく
|
||||
• 異なる絵文字形式を選択可能
|
||||
• すべての画面サイズに最適化
|
||||
• 完全にオープンソース - Googleサービスのような自由でない依存関係はありません
|
||||
|
||||
Mastodonの詳細についてはこちらをご覧ください:https://joinmastodon.org/
|
1
fastlane/metadata/android/kab/short_description.txt
Normal file
1
fastlane/metadata/android/kab/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
Asnas isefraken aget-imiḍanen n uẓeṭṭa n tmetti Mastodon
|
1
fastlane/metadata/android/kab/title.txt
Normal file
1
fastlane/metadata/android/kab/title.txt
Normal file
@ -0,0 +1 @@
|
||||
Tusky
|
8
fastlane/metadata/android/pl/changelogs/70.txt
Normal file
8
fastlane/metadata/android/pl/changelogs/70.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Tusky v10.0
|
||||
|
||||
- Dodano wsparcie dla zakładek. Możesz teraz dodawać wpisy do zakładek i przeglądać zakładki w Tusky.
|
||||
- Możesz teraz zaplanować wpisy w Tusky. (Uwaga: wybrany czas do wysłania zaplanowanego wpisu musi wynosić przynajmniej 5 minut)
|
||||
- Możesz teraz dodawać listy do ekranu głównego.
|
||||
- Możesz teraz załączać pliki audio do wpisów.
|
||||
|
||||
Plus wiele innych małych ulepszeń i poprawek!
|
8
fastlane/metadata/android/ru/changelogs/70.txt
Normal file
8
fastlane/metadata/android/ru/changelogs/70.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Tusky v10.0
|
||||
|
||||
- Теперь вы можете добавлять статусы в закладки и просматривать списки закладок в Tusky.
|
||||
- Теперь вы можете отправлять отложенные посты из Tusky. Учтите, что время отправки должно быть не менее, чем на пять минут позже текущего.
|
||||
- Теперь вы можете добавлять списки на главный экран.
|
||||
- Теперь вы можете публиковать аудио-вложения из Tusky.
|
||||
|
||||
И множество других мелких улучшений и исправлений!
|
@ -15,5 +15,5 @@ org.gradle.jvmargs=-Xmx4096m
|
||||
# use parallel execution
|
||||
org.gradle.parallel=true
|
||||
|
||||
android.enableUnitTestBinaryResources=true
|
||||
android.enableR8.fullMode=true
|
||||
android.useAndroidX=true
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
Loading…
Reference in New Issue
Block a user