forked from mirror/monocles_chat_clean
update fork #128
11 changed files with 209 additions and 111 deletions
commit
56a17c1e07
|
|
@ -927,7 +927,7 @@ public class IqGenerator extends AbstractGenerator {
|
|||
return iq;
|
||||
}
|
||||
|
||||
public Iq publishComment(final Account account, final String node, final String title, final String inReplyToId) {
|
||||
public Iq publishComment(final Account account, final String node, final String title) {
|
||||
final Iq iq = new Iq(Iq.Type.SET);
|
||||
iq.setTo(account.getJid().asBareJid());
|
||||
final Element pubsub = iq.addChild("pubsub", Namespace.PUBSUB);
|
||||
|
|
|
|||
|
|
@ -8225,7 +8225,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
}
|
||||
|
||||
public void publishComment(final Account account, final String nodeUri, final String title, final String inReplyToId, final OnPostPublished callback) {
|
||||
public void publishComment(final Account account, final String nodeUri, final String title, final OnPostPublished callback) {
|
||||
if (account == null) {
|
||||
if (callback != null) {
|
||||
callback.onPostPublishFailed();
|
||||
|
|
@ -8263,7 +8263,7 @@ public class XmppConnectionService extends Service {
|
|||
Log.d(Config.LOGTAG, "could not create comments node " + response);
|
||||
}
|
||||
}
|
||||
final Iq publishRequest = getIqGenerator().publishComment(account, targetNode, title, inReplyToId);
|
||||
final Iq publishRequest = getIqGenerator().publishComment(account, targetNode, title);
|
||||
publishRequest.setTo(to);
|
||||
sendIqPacket(account, publishRequest, publishResponse -> {
|
||||
if (publishResponse.getType() == Iq.Type.RESULT) {
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ public class CreatePostActivity extends XmppActivity {
|
|||
}
|
||||
|
||||
if (inReplyToNode != null) {
|
||||
xmppConnectionService.publishComment(selectedAccount, inReplyToNode, content, inReplyToId, new XmppConnectionService.OnPostPublished() {
|
||||
xmppConnectionService.publishComment(selectedAccount, inReplyToNode, content, new XmppConnectionService.OnPostPublished() {
|
||||
@Override
|
||||
public void onPostPublished() {
|
||||
runOnUiThread(() -> {
|
||||
|
|
|
|||
|
|
@ -162,23 +162,44 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
}
|
||||
|
||||
void bind(Post post) {
|
||||
// Always reset the state of recycled views to a clean, default state before binding
|
||||
binding.commentsList.setVisibility(View.GONE);
|
||||
binding.commentsList.setAdapter(null);
|
||||
binding.likeButton.setOnClickListener(null);
|
||||
binding.likeCount.setText("");
|
||||
binding.likeButton.setEnabled(false); // Disable until content is loaded
|
||||
binding.likeButton.setCompoundDrawablesWithIntrinsicBounds(R.drawable.outline_favorite_border_24, 0, 0, 0);
|
||||
|
||||
final boolean isExpanded = expandedPosts.contains(post);
|
||||
|
||||
// Set visibility of summary/full content based on expanded state
|
||||
binding.postContentSummary.setVisibility(isExpanded ? View.GONE : View.VISIBLE);
|
||||
binding.postContentFull.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
|
||||
binding.postActions.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
|
||||
if (isExpanded && post.getCommentsNode() != null) {
|
||||
loadComments(post, binding.commentsList);
|
||||
} else {
|
||||
binding.commentsList.setVisibility(View.GONE);
|
||||
binding.commentsList.setAdapter(null);
|
||||
|
||||
if (isExpanded) {
|
||||
// Asynchronously load all dynamic content (comments and likes)
|
||||
loadCommentsAndLikes(post, this);
|
||||
}
|
||||
|
||||
final View.OnClickListener expandClickListener = v -> {
|
||||
if (expandedPosts.contains(post)) {
|
||||
expandedPosts.remove(post);
|
||||
} else {
|
||||
expandedPosts.add(post);}
|
||||
notifyItemChanged(getAdapterPosition());
|
||||
};
|
||||
|
||||
// This is the full, non-omitted logic to bind the main post view
|
||||
setupPostView(post, expandClickListener);
|
||||
}
|
||||
|
||||
private void setupPostView(final Post post, final View.OnClickListener expandClickListener) {
|
||||
final boolean isExpanded = expandedPosts.contains(post);
|
||||
final boolean hasAttachment = post.getAttachmentUrl() != null;
|
||||
final boolean isImage = hasAttachment && post.getAttachmentType() != null && post.getAttachmentType().startsWith("image/");
|
||||
final boolean isVideo = hasAttachment && post.getAttachmentType() != null && post.getAttachmentType().startsWith("video/");
|
||||
|
||||
binding.postContentSummary.setVisibility(isExpanded ? View.GONE : View.VISIBLE);
|
||||
binding.postContentFull.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
|
||||
binding.postActions.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
|
||||
binding.attachmentHint.setVisibility(hasAttachment && !isExpanded ? View.VISIBLE : View.GONE);
|
||||
binding.postImage.setVisibility(isExpanded && (isImage || isVideo) ? View.VISIBLE : View.GONE);
|
||||
binding.videoOverlayIcon.setVisibility(isExpanded && isVideo ? View.VISIBLE : View.GONE);
|
||||
|
|
@ -186,43 +207,24 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
|
||||
if (isExpanded && (isImage || isVideo)) {
|
||||
binding.attachmentProgress.setVisibility(View.VISIBLE);
|
||||
if (isImage) {
|
||||
Glide.with(mActivity)
|
||||
.load(post.getAttachmentUrl())
|
||||
.listener(new com.bumptech.glide.request.RequestListener<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable com.bumptech.glide.load.engine.GlideException e, @Nullable Object model, @NonNull com.bumptech.glide.request.target.Target<Drawable> target, boolean isFirstResource) {
|
||||
binding.attachmentProgress.setVisibility(View.GONE);
|
||||
return false;
|
||||
}
|
||||
Glide.with(mActivity)
|
||||
.load(post.getAttachmentUrl()) .listener(new com.bumptech.glide.request.RequestListener<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable com.bumptech.glide.load.engine.GlideException e, @Nullable Object model, @NonNull com.bumptech.glide.request.target.Target<Drawable> target, boolean isFirstResource) {
|
||||
binding.attachmentProgress.setVisibility(View.GONE);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, @NonNull com.bumptech.glide.request.target.Target<Drawable> target, @NonNull com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
|
||||
binding.attachmentProgress.setVisibility(View.GONE);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(binding.postImage);
|
||||
binding.postImage.setOnClickListener(v -> showImagePreviewDialog(post.getAttachmentUrl()));
|
||||
} else {
|
||||
Glide.with(mActivity)
|
||||
.load(post.getAttachmentUrl())
|
||||
.listener(new com.bumptech.glide.request.RequestListener<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable com.bumptech.glide.load.engine.GlideException e, @Nullable Object model, @NonNull com.bumptech.glide.request.target.Target<Drawable> target, boolean isFirstResource) {
|
||||
binding.attachmentProgress.setVisibility(View.GONE);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, @NonNull com.bumptech.glide.request.target.Target<Drawable> target, @NonNull com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
|
||||
binding.attachmentProgress.setVisibility(View.GONE);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(binding.postImage);
|
||||
binding.postImage.setOnClickListener(v -> showVideoPreviewDialog(post.getAttachmentUrl()));
|
||||
}
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, @NonNull com.bumptech.glide.request.target.Target<Drawable> target, @NonNull com.bumptech.glide.load.DataSource dataSource, boolean isFirstResource) {
|
||||
binding.attachmentProgress.setVisibility(View.GONE);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(binding.postImage);binding.postImage.setOnClickListener(v -> {
|
||||
if (isImage) showImagePreviewDialog(post.getAttachmentUrl());
|
||||
else showVideoPreviewDialog(post.getAttachmentUrl());
|
||||
});
|
||||
}
|
||||
|
||||
final List<Account> postAccounts = new ArrayList<>();
|
||||
|
|
@ -231,8 +233,7 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
if (account.isOnlineAndConnected()) {
|
||||
if (account.getJid().asBareJid().equals(post.getAuthor().asBareJid()) || (account.getRoster() != null && account.getRoster().getContact(post.getAuthor().asBareJid()) != null)) {
|
||||
postAccounts.add(account);
|
||||
}
|
||||
}
|
||||
} }
|
||||
}
|
||||
}
|
||||
if (mActivity.xmppConnectionService == null) {
|
||||
|
|
@ -241,58 +242,23 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
binding.replyButton.setVisibility(View.GONE);
|
||||
binding.commentButton.setVisibility(View.GONE);
|
||||
binding.downloadButton.setVisibility(View.GONE);
|
||||
binding.likeButton.setVisibility(View.GONE);
|
||||
} else {
|
||||
final Account ownAccount = post.getAuthor() != null ? mActivity.xmppConnectionService.findAccountByJid(post.getAuthor().asBareJid()) : null;
|
||||
|
||||
|
||||
binding.downloadButton.setOnClickListener(v -> {
|
||||
if (postAccounts.size() == 1) {
|
||||
downloadAttachment(postAccounts.get(0), post);
|
||||
} else {
|
||||
showAccountSelectionDialog(mActivity.getString(R.string.choose_account_for_download), postAccounts, account -> downloadAttachment(account, post));
|
||||
} else { showAccountSelectionDialog(mActivity.getString(R.string.choose_account_for_download), postAccounts, account -> downloadAttachment(account, post));
|
||||
}
|
||||
});
|
||||
|
||||
if (post.getContent() != null) {
|
||||
final CharSequence markdown = markwon.toMarkdown(post.getContent());
|
||||
final SpannableString spannable = new SpannableString(markdown);
|
||||
final Matcher matcher = Pattern.compile("#[\\p{L}\\p{N}]+").matcher(spannable);
|
||||
while (matcher.find()) {
|
||||
final String hashtag = matcher.group(0);
|
||||
spannable.setSpan(new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(@NonNull View widget) {
|
||||
mOnSearchPerformed.onSearchPerformed(hashtag);
|
||||
}
|
||||
}, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
binding.postContentSummary.setText(spannable);
|
||||
binding.postContentFull.setText(spannable);
|
||||
binding.postContentSummary.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
|
||||
binding.postContentFull.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
|
||||
} else {binding.postContentSummary.setText("");
|
||||
binding.postContentFull.setText("");
|
||||
}
|
||||
|
||||
final View.OnClickListener expandClickListener = v -> {
|
||||
if (expandedPosts.contains(post)) {
|
||||
expandedPosts.remove(post);
|
||||
} else {
|
||||
expandedPosts.add(post);
|
||||
}
|
||||
notifyItemChanged(getAdapterPosition());
|
||||
};
|
||||
|
||||
if (post.getContent() != null) {
|
||||
final CharSequence markdown = markwon.toMarkdown(post.getContent());
|
||||
|
||||
// For the summary view, just set the text and an expand listener. No clickable spans.
|
||||
binding.postContentSummary.setText(markdown);
|
||||
binding.postContentSummary.setMovementMethod(null);
|
||||
binding.postContentSummary.setOnClickListener(expandClickListener);
|
||||
|
||||
|
||||
// For the full view, make hashtags clickable.
|
||||
final SpannableString spannable = new SpannableString(markdown);
|
||||
final Matcher matcher = Pattern.compile("#[\\p{L}\\p{N}]+").matcher(spannable);
|
||||
while (matcher.find()) {
|
||||
|
|
@ -304,9 +270,10 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
}
|
||||
}, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
binding.postContentFull.setText(spannable);
|
||||
binding.postContentFull.setMovementMethod(android.text.method.LinkMovementMethod.getInstance());
|
||||
binding.postContentFull.setOnClickListener(null); // Clicks handled by spans, not the view itself
|
||||
binding.postContentFull.setOnClickListener(null);
|
||||
|
||||
} else {
|
||||
binding.postContentSummary.setText("");
|
||||
|
|
@ -315,7 +282,6 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
|
||||
itemView.setOnClickListener(expandClickListener);
|
||||
binding.postTitle.setOnClickListener(expandClickListener);
|
||||
binding.postContentSummary.setOnClickListener(expandClickListener);
|
||||
|
||||
binding.replyButton.setOnClickListener(v -> {
|
||||
if (post.getAuthor() != null) {
|
||||
|
|
@ -332,7 +298,7 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
binding.shareButton.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, post.getTitle() + "\\n" + post.getContent());
|
||||
intent.putExtra(Intent.EXTRA_TEXT, post.getTitle() + "\n" + post.getContent());
|
||||
mActivity.startActivity(Intent.createChooser(intent, mActivity.getString(R.string.share_post_with)));
|
||||
});
|
||||
|
||||
|
|
@ -430,9 +396,10 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
}
|
||||
}
|
||||
|
||||
private void loadComments(final Post post, final RecyclerView recyclerView) {if (mActivity.xmppConnectionService == null) {
|
||||
return;
|
||||
}
|
||||
private void loadCommentsAndLikes(final Post post, final PostViewHolder holder) {
|
||||
if (mActivity.xmppConnectionService == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final XmppUri uri = new XmppUri(post.getCommentsNode());
|
||||
final Jid jid = uri.getJid();
|
||||
|
|
@ -446,6 +413,7 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
reader.setInputStream(new java.io.ByteArrayInputStream(feedXml.getBytes()));
|
||||
final Element pubsub = reader.readElement(reader.readTag());
|
||||
final List<Comment> comments = new ArrayList<>();
|
||||
final List<Comment> likes = new ArrayList<>();
|
||||
if (pubsub != null) {
|
||||
final Element items = pubsub.findChild("items");
|
||||
if (items != null) {
|
||||
|
|
@ -453,34 +421,39 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
if ("item".equals(item.getName())) {
|
||||
final Comment comment = Comment.fromElement(item);
|
||||
if (comment != null) {
|
||||
comments.add(comment);
|
||||
if ("♥".equals(comment.getTitle())) {
|
||||
likes.add(comment);
|
||||
} else {
|
||||
comments.add(comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mActivity.runOnUiThread(() -> {
|
||||
if (holder.getAdapterPosition() == RecyclerView.NO_POSITION || posts.get(holder.getAdapterPosition()) != post) {
|
||||
return; // Don't update a recycled view
|
||||
}
|
||||
// Update comments list UI
|
||||
if (!comments.isEmpty()) {
|
||||
java.util.Collections.sort(comments, (c1, c2) -> {
|
||||
Date d1 = c1.getPublished();
|
||||
Date d2 = c2.getPublished();
|
||||
if (d1 == null && d2 == null) {
|
||||
return 0;
|
||||
} else if (d1 == null) {
|
||||
return -1;
|
||||
} else if (d2 == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return d1.compareTo(d2);
|
||||
}
|
||||
if (d1 == null && d2 == null) return 0;
|
||||
if (d1 == null) return -1;
|
||||
if (d2 == null) return 1;
|
||||
return d1.compareTo(d2);
|
||||
});
|
||||
CommentsAdapter commentsAdapter = new CommentsAdapter(mActivity, post, comments);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
|
||||
recyclerView.setAdapter(commentsAdapter);
|
||||
recyclerView.setVisibility(View.VISIBLE);
|
||||
holder.binding.commentsList.setLayoutManager(new LinearLayoutManager(mActivity));
|
||||
holder.binding.commentsList.setAdapter(commentsAdapter);
|
||||
holder.binding.commentsList.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
holder.binding.commentsList.setVisibility(View.GONE);
|
||||
}
|
||||
// Update like button UI and logic
|
||||
setupLikeButton(post, holder, likes);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e(Config.LOGTAG, "error parsing comments", e);
|
||||
|
|
@ -497,9 +470,92 @@ public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.PostViewHold
|
|||
Log.e(Config.LOGTAG, "error parsing comments node uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupLikeButton(final Post post, final PostViewHolder holder, final List<Comment> likes) {
|
||||
final List<Account> onlineAccounts = mActivity.xmppConnectionService.getAccounts().stream()
|
||||
.filter(Account::isOnlineAndConnected)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
if (onlineAccounts.isEmpty()) {
|
||||
holder.binding.likeButton.setEnabled(false);
|
||||
holder.binding.likeCount.setText(String.valueOf(likes.size()));
|
||||
return;
|
||||
}
|
||||
holder.binding.likeButton.setEnabled(true);
|
||||
holder.binding.likeCount.setText(String.valueOf(likes.size()));
|
||||
|
||||
Comment myLike = null;
|
||||
Account myLikerAccount = null;
|
||||
for (Account acc : onlineAccounts) {
|
||||
for (Comment like : likes) {
|
||||
if (like.getAuthor() != null && like.getAuthor().asBareJid().equals(acc.getJid().asBareJid())) {
|
||||
myLike = like;
|
||||
myLikerAccount = acc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (myLike != null) break;
|
||||
}
|
||||
|
||||
final boolean hasLiked = myLike != null;
|
||||
holder.binding.likeButton.setCompoundDrawablesWithIntrinsicBounds(
|
||||
hasLiked ? R.drawable.outline_favorite_24 : R.drawable.outline_favorite_border_24, 0, 0, 0);
|
||||
|
||||
final Comment finalMyLike = myLike;
|
||||
final Account finalMyLikerAccount = myLikerAccount;
|
||||
holder.binding.likeButton.setOnClickListener(v -> {
|
||||
if (hasLiked && finalMyLikerAccount != null) {
|
||||
// Unlike is simple: use the account that was found to have liked the post
|
||||
retractLike(finalMyLikerAccount, finalMyLike, post, holder);
|
||||
} else {
|
||||
// Like: if there are multiple accounts, let the user choose
|
||||
if (onlineAccounts.size() > 1) {
|
||||
showAccountSelectionDialog(mActivity.getString(R.string.choose_account_for_like), onlineAccounts, selectedAccount -> {
|
||||
publishLike(selectedAccount, post, holder);
|
||||
});
|
||||
} else {
|
||||
publishLike(onlineAccounts.get(0), post, holder);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void publishLike(final Account account, final Post post, final PostViewHolder holder) {
|
||||
mActivity.xmppConnectionService.publishComment(account, post.getCommentsNode(), "♥", new XmppConnectionService.OnPostPublished() {
|
||||
@Override
|
||||
public void onPostPublished() {
|
||||
mActivity.runOnUiThread(() -> loadCommentsAndLikes(post, holder));
|
||||
}
|
||||
@Override
|
||||
public void onPostPublishFailed() {
|
||||
mActivity.runOnUiThread(() -> Toast.makeText(mActivity, R.string.error_liking_post, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void retractLike(final Account account, final Comment like, final Post post, final PostViewHolder holder) {
|
||||
try {
|
||||
final XmppUri uri = new XmppUri(post.getCommentsNode());
|
||||
final Jid jid = uri.getJid();
|
||||
final String node = uri.getParameter("node");
|
||||
mActivity.xmppConnectionService.retractPost(account, jid, node, like.getId(), new XmppConnectionService.OnPostRetracted() {
|
||||
@Override
|
||||
public void onPostRetracted(String postId) {
|
||||
mActivity.runOnUiThread(() -> loadCommentsAndLikes(post, holder));
|
||||
}
|
||||
@Override
|
||||
public void onPostRetractionFailed() {
|
||||
mActivity.runOnUiThread(() -> Toast.makeText(mActivity, R.string.error_removing_like, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e(Config.LOGTAG, "error retracting like", e);
|
||||
mActivity.runOnUiThread(() -> Toast.makeText(mActivity, R.string.error_removing_like, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadAttachment(Account account, Post post) {
|
||||
private void downloadAttachment(Account account, Post post) {
|
||||
if (mActivity.xmppConnectionService == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
5
src/main/res/drawable/delete_18dp.xml
Normal file
5
src/main/res/drawable/delete_18dp.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="18dp" android:tint="?colorControlNormal" android:viewportHeight="960" android:viewportWidth="960" android:width="18dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M280,840Q247,840 223.5,816.5Q200,793 200,760L200,240L160,240L160,160L360,160L360,120L600,120L600,160L800,160L800,240L760,240L760,760Q760,793 736.5,816.5Q713,840 680,840L280,840ZM680,240L280,240L280,760Q280,760 280,760Q280,760 280,760L680,760Q680,760 680,760Q680,760 680,760L680,240ZM360,680L440,680L440,320L360,320L360,680ZM520,680L600,680L600,320L520,320L520,680ZM280,240L280,240L280,760Q280,760 280,760Q280,760 280,760L280,760Q280,760 280,760Q280,760 280,760L280,240Z"/>
|
||||
|
||||
</vector>
|
||||
5
src/main/res/drawable/outline_favorite_24.xml
Normal file
5
src/main/res/drawable/outline_favorite_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
|
||||
|
||||
</vector>
|
||||
5
src/main/res/drawable/outline_favorite_border_24.xml
Normal file
5
src/main/res/drawable/outline_favorite_border_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
|
||||
|
||||
</vector>
|
||||
5
src/main/res/drawable/outline_heart_plus_24.xml
Normal file
5
src/main/res/drawable/outline_heart_plus_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="960" android:viewportWidth="960" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459L440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459Q440,459 440,459ZM440,840L313,726Q241,661 189.5,610Q138,559 104.5,514Q71,469 55.5,427Q40,385 40,339Q40,245 103,182.5Q166,120 260,120Q312,120 359,142Q406,164 440,204Q474,164 521,142Q568,120 620,120Q701,120 756,165.5Q811,211 831,280Q831,280 817.5,280Q804,280 788.5,280Q773,280 759.5,280Q746,280 746,280Q728,240 693,220Q658,200 620,200Q569,200 532,227.5Q495,255 463,300L417,300Q386,255 346.5,227.5Q307,200 260,200Q203,200 161.5,239.5Q120,279 120,339Q120,372 134,406Q148,440 184,484.5Q220,529 282,588.5Q344,648 440,732Q466,709 501,679Q536,649 557,629Q557,629 566,638Q575,647 585.5,657.5Q596,668 605,677Q614,686 614,686Q592,706 558,735.5Q524,765 498,788L440,840ZM720,680L720,560L600,560L600,480L720,480L720,360L800,360L800,480L920,480L920,560L800,560L800,680L720,680Z"/>
|
||||
|
||||
</vector>
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/retract_comment"
|
||||
android:src="@drawable/ic_delete_white_24dp"
|
||||
android:src="@drawable/delete_18dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/comment_author_name"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/comment_author_name"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
android:id="@+id/post_timestamp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android.layout_marginStart="8dp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
|
@ -138,7 +138,7 @@
|
|||
android:layout_marginTop="8dp"
|
||||
android:text="@string/download_file"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/post_title"
|
||||
app:layout_constraintTop_toBottomOf="@+id/post_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
|
@ -182,6 +182,22 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/post_content_full"
|
||||
tools:visibility="visible">
|
||||
|
||||
<Button
|
||||
android:id="@+id/like_button"
|
||||
style="@style/Widget.Material3.Button.TextButton.Icon"
|
||||
android:layout_width="42dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/outline_favorite_border_24" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/like_count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="-8dp"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
tools:text="5" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/reply_button"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
|
|
|
|||
|
|
@ -1638,6 +1638,12 @@
|
|||
<string name="retract_comment">Retract comment</string>
|
||||
<string name="retract_comment_confirm">Do you really want to retract this comment?</string>
|
||||
<string name="error_retracting_comment">Could not retract comment</string>
|
||||
<string name="like">Like</string>
|
||||
<string name="like_added">Like added</string>
|
||||
<string name="error_liking_post">Could not like post</string>
|
||||
<string name="like_removed">Like removed</string>
|
||||
<string name="error_removing_like">Could not remove like</string>
|
||||
<string name="choose_account_for_like">Like post with</string>
|
||||
<plurals name="publishing_to_x_contacts">
|
||||
<item quantity="one">Publishing to %d contact</item>
|
||||
<item quantity="other">Publishing to %d contacts</item>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue