From 65156e0a84d38c7e76218146dbfd18b36e7cc0b3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 4 Dec 2022 18:25:10 +0100 Subject: [PATCH 01/31] Fix issue when refreshing notifications + fix an issue with cache and DM --- .../timeline/FragmentMastodonConversation.java | 17 +++++++++++------ .../timeline/FragmentMastodonNotification.java | 12 ++++++++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java index 2262edd1..ded9e0af 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonConversation.java @@ -140,7 +140,7 @@ public class FragmentMastodonConversation extends Fragment implements Conversati timelineParams.fetchingMissing = fetchingMissing; - if (useCache) { + if (useCache && direction != FragmentMastodonTimeline.DIRECTION.SCROLL_TOP && direction != FragmentMastodonTimeline.DIRECTION.FETCH_NEW) { getCachedConversations(direction, fetchingMissing, timelineParams); } else { getLiveConversations(direction, fetchingMissing, timelineParams, conversationToUpdate); @@ -150,7 +150,7 @@ public class FragmentMastodonConversation extends Fragment implements Conversati private void getCachedConversations(FragmentMastodonTimeline.DIRECTION direction, boolean fetchingMissing, TimelinesVM.TimelineParams timelineParams) { if (direction == null) { - timelinesVM.getConversations(conversationList, timelineParams) + timelinesVM.getConversationsCache(conversationList, timelineParams) .observe(getViewLifecycleOwner(), conversationsCached -> { if (conversationsCached == null || conversationsCached.conversations == null || conversationsCached.conversations.size() == 0) { getLiveConversations(null, fetchingMissing, timelineParams, null); @@ -179,13 +179,18 @@ public class FragmentMastodonConversation extends Fragment implements Conversati } }); } else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) { - timelinesVM.getConversations(conversationList, timelineParams) + timelinesVM.getConversationsCache(conversationList, timelineParams) .observe(getViewLifecycleOwner(), notificationsRefresh -> { - if (conversationAdapter != null) { - dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, null); + if (notificationsRefresh == null || notificationsRefresh.conversations == null || notificationsRefresh.conversations.size() == 0) { + getLiveConversations(direction, fetchingMissing, timelineParams, null); } else { - initializeConversationCommonView(notificationsRefresh); + if (conversationAdapter != null) { + dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, null); + } else { + initializeConversationCommonView(notificationsRefresh); + } } + }); } } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java index 3a07d55e..0bef27f8 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonNotification.java @@ -387,7 +387,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } timelineParams.excludeType = getExcludeType(); timelineParams.fetchingMissing = fetchingMissing; - if (useCache) { + if (useCache && direction != FragmentMastodonTimeline.DIRECTION.SCROLL_TOP && direction != FragmentMastodonTimeline.DIRECTION.FETCH_NEW) { getCachedNotifications(direction, fetchingMissing, timelineParams); } else { getLiveNotifications(direction, fetchingMissing, timelineParams, notificationToUpdate); @@ -430,10 +430,14 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } else if (direction == FragmentMastodonTimeline.DIRECTION.REFRESH) { notificationsVM.getNotifications(notificationList, timelineParams) .observe(getViewLifecycleOwner(), notificationsRefresh -> { - if (notificationAdapter != null) { - dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, null); + if (notificationsRefresh == null || notificationsRefresh.notifications == null || notificationsRefresh.notifications.size() == 0) { + getLiveNotifications(direction, fetchingMissing, timelineParams, null); } else { - initializeNotificationView(notificationsRefresh); + if (notificationAdapter != null) { + dealWithPagination(notificationsRefresh, FragmentMastodonTimeline.DIRECTION.REFRESH, true, null); + } else { + initializeNotificationView(notificationsRefresh); + } } }); } From 427073a369b00a8da7bfdea6e59a7621a5d951f0 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 4 Dec 2022 18:39:41 +0100 Subject: [PATCH 02/31] Fix issue #591 --- app/src/main/res/values-night/themes.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 031cb493..72cf8611 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -224,7 +224,7 @@ @color/white @color/md_theme_dark_secondary @color/md_theme_dark_onSecondary - @color/black + @color/md_theme_dark_secondaryContainer @color/md_theme_dark_onSecondaryContainer @color/md_theme_dark_tertiary @color/md_theme_dark_onTertiary From 0c6fe112c974f13ae194c1c055171e4fb480c28b Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 4 Dec 2022 18:46:51 +0100 Subject: [PATCH 03/31] Fix issue #592 - Second search clears term field --- .../fedilab/android/activities/SearchResultTabActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/fedilab/android/activities/SearchResultTabActivity.java b/app/src/main/java/app/fedilab/android/activities/SearchResultTabActivity.java index 5b27f224..2e6584a8 100644 --- a/app/src/main/java/app/fedilab/android/activities/SearchResultTabActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/SearchResultTabActivity.java @@ -130,9 +130,10 @@ public class SearchResultTabActivity extends BaseBarActivity { search = query.trim(); ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); binding.searchViewpager.setAdapter(mPagerAdapter); - searchView.clearFocus(); setTitle(search); searchView.setIconified(true); + searchView.setQuery(search, false); + searchView.clearFocus(); binding.searchTabLayout.selectTab(initial); return false; } From ffb6596c994910343e21dc3f73a0acc70149ae5e Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 5 Dec 2022 09:01:37 +0100 Subject: [PATCH 04/31] Fix issue #598 - Avoid jumps with fit preview images. --- .../android/ui/drawer/StatusAdapter.java | 59 ++++++++++++++++--- app/src/main/res/layout/layout_media.xml | 3 +- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java index ddfaeddf..80dd507d 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java @@ -1119,20 +1119,41 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.mediaContainer.setVisibility(View.VISIBLE); holder.binding.displayMedia.setVisibility(View.GONE); LayoutMediaBinding layoutMediaBinding = LayoutMediaBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false); - LinearLayout.LayoutParams lp; + if (fullAttachement) { - lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - layoutMediaBinding.media.setScaleType(ImageView.ScaleType.FIT_CENTER); + layoutMediaBinding.media.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + LinearLayout.LayoutParams lp; + layoutMediaBinding.media.getViewTreeObserver().removeOnGlobalLayoutListener(this); + if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.small != null) { + float viewWidth = layoutMediaBinding.media.getWidth(); + float mediaH = statusToDeal.media_attachments.get(0).meta.small.height; + float mediaW = statusToDeal.media_attachments.get(0).meta.small.width; + float ratio = 1.0f; + if (mediaW != 0) { + ratio = viewWidth / mediaW; + } + lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (mediaH * ratio)); + } else { + lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + } + layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + layoutMediaBinding.media.setLayoutParams(lp); + } + }); } else { + LinearLayout.LayoutParams lp; lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) Helper.convertDpToPixel(200, context)); layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_CROP); + layoutMediaBinding.media.setLayoutParams(lp); } if (statusToDeal.sensitive) { Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, ThemeHelper.getAttColor(context, R.attr.colorError)); } else { Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, R.color.white); } - layoutMediaBinding.media.setLayoutParams(lp); + layoutMediaBinding.media.setOnClickListener(v -> { if (statusToDeal.isMediaObfuscated && mediaObfuscated(statusToDeal) && !expand_media) { statusToDeal.isMediaObfuscated = false; @@ -1233,7 +1254,7 @@ public class StatusAdapter extends RecyclerView.Adapter int mediaPosition = 1; for (Attachment attachment : statusToDeal.media_attachments) { LayoutMediaBinding layoutMediaBinding = LayoutMediaBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false); - LinearLayout.LayoutParams lp; + float focusX = 0.f; float focusY = 0.f; if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.focus != null) { @@ -1257,11 +1278,33 @@ public class StatusAdapter extends RecyclerView.Adapter return true; }); if (fullAttachement) { - lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); - layoutMediaBinding.media.setScaleType(ImageView.ScaleType.FIT_CENTER); + layoutMediaBinding.media.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + LinearLayout.LayoutParams lp; + layoutMediaBinding.media.getViewTreeObserver().removeOnGlobalLayoutListener(this); + if (attachment.meta != null && attachment.meta.small != null) { + float viewWidth = layoutMediaBinding.media.getWidth(); + float mediaH = attachment.meta.small.height; + float mediaW = attachment.meta.small.width; + float ratio = 1.0f; + if (mediaW != 0) { + ratio = viewWidth / mediaW; + } + lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (mediaH * ratio)); + } else { + lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + } + layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + layoutMediaBinding.media.setLayoutParams(lp); + } + }); + } else { + LinearLayout.LayoutParams lp; lp = new LinearLayout.LayoutParams((int) Helper.convertDpToPixel(200, context), (int) Helper.convertDpToPixel(200, context)); layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_CROP); + layoutMediaBinding.media.setLayoutParams(lp); } if (attachment.type != null && (attachment.type.equalsIgnoreCase("video") || attachment.type.equalsIgnoreCase("gifv"))) { layoutMediaBinding.playVideo.setVisibility(View.VISIBLE); @@ -1299,7 +1342,7 @@ public class StatusAdapter extends RecyclerView.Adapter } else { Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, R.color.white); } - layoutMediaBinding.media.setLayoutParams(lp); + int finalMediaPosition = mediaPosition; layoutMediaBinding.media.setOnClickListener(v -> { Intent mediaIntent = new Intent(context, MediaActivity.class); diff --git a/app/src/main/res/layout/layout_media.xml b/app/src/main/res/layout/layout_media.xml index fa259a3b..3788c1b6 100644 --- a/app/src/main/res/layout/layout_media.xml +++ b/app/src/main/res/layout/layout_media.xml @@ -12,8 +12,7 @@ app:layout_constraintTop_toTopOf="parent" android:id="@+id/media" android:layout_width="match_parent" - android:layout_height="200dp" - android:scaleType="centerCrop" + android:layout_height="wrap_content" tools:ignore="ContentDescription" /> Date: Mon, 5 Dec 2022 16:23:13 +0100 Subject: [PATCH 05/31] Fix issue #288 - Custom Emoji not always displayed --- .../android/client/entities/api/Status.java | 30 ++++++++++++---- .../fedilab/android/helper/CustomEmoji.java | 36 +++++++++++++++++-- .../android/helper/SpannableHelper.java | 29 ++++++--------- .../android/ui/drawer/ComposeAdapter.java | 4 +-- .../ui/drawer/ConversationAdapter.java | 4 +-- .../ui/drawer/NotificationAdapter.java | 10 +++++- .../android/ui/drawer/StatusAdapter.java | 28 ++++++++++++--- .../ui/drawer/StatusHistoryAdapter.java | 4 +-- 8 files changed, 106 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java index e3c75305..77302283 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Status.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Status.java @@ -115,6 +115,10 @@ public class Status implements Serializable, Cloneable { public transient boolean submitted = false; public transient boolean spoilerChecked = false; public Filter filteredByApp; + public transient Spannable contentSpan; + public transient Spannable contentSpoilerSpan; + public transient Spannable contentTranslateSpan; + @Override public boolean equals(@Nullable Object obj) { boolean same = false; @@ -124,17 +128,29 @@ public class Status implements Serializable, Cloneable { return same; } - public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference); + public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference, Callback callback) { + if (contentSpan == null) { + contentSpan = SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference, callback); + } + return contentSpan; } - - public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference); + public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference, Callback callback) { + if (contentSpoilerSpan == null) { + contentSpoilerSpan = SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference, callback); + } + return contentSpoilerSpan; } - public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference); + public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference, Callback callback) { + if (contentTranslateSpan == null) { + contentTranslateSpan = SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference, callback); + } + return contentTranslateSpan; + } + + public interface Callback { + void emojiFetched(); } @NonNull diff --git a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java index 10835111..39689d82 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -6,6 +6,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; +import android.text.SpannableStringBuilder; import android.text.style.ReplacementSpan; import android.view.View; @@ -13,26 +14,53 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; +import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.transition.Transition; import java.lang.ref.WeakReference; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import app.fedilab.android.R; +import app.fedilab.android.client.entities.api.Emoji; +import app.fedilab.android.client.entities.api.Status; public class CustomEmoji extends ReplacementSpan { private final float scale; private final WeakReference viewWeakReference; private Drawable imageDrawable; - + private boolean callbackCalled; CustomEmoji(WeakReference viewWeakReference) { Context mContext = viewWeakReference.get().getContext(); this.viewWeakReference = viewWeakReference; SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.1f); + callbackCalled = false; + } + + public SpannableStringBuilder makeEmoji(SpannableStringBuilder content, List emojiList, boolean animate, Status.Callback callback) { + if (emojiList != null && emojiList.size() > 0) { + int count = 1; + for (Emoji emoji : emojiList) { + Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) + .matcher(content); + while (matcher.find()) { + CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(viewWeakReference.get())); + content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); + Glide.with(viewWeakReference.get()) + .asDrawable() + .load(animate ? emoji.url : emoji.static_url) + .into(customEmoji.getTarget(animate, count == emojiList.size() && !callbackCalled ? callback : null)); + } + count++; + } + } + return content; } @Override @@ -61,7 +89,7 @@ public class CustomEmoji extends ReplacementSpan { } } - public Target getTarget(boolean animate) { + public Target getTarget(boolean animate, Status.Callback callback) { return new CustomTarget() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { @@ -97,6 +125,10 @@ public class CustomEmoji extends ReplacementSpan { if (view != null) { view.invalidate(); } + if (callback != null) { + callbackCalled = true; + callback.emojiFetched(); + } } @Override diff --git a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java index 455614b3..af12f71d 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -89,10 +89,16 @@ public class SpannableHelper { public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; + public static Spannable convert(Context context, String text, + Status status, Account account, Announcement announcement, + boolean convertHtml, WeakReference viewWeakReference) { + return convert(context, text, status, account, announcement, convertHtml, viewWeakReference, null); + } + public static Spannable convert(Context context, String text, Status status, Account account, Announcement announcement, boolean convertHtml, - WeakReference viewWeakReference) { + WeakReference viewWeakReference, Status.Callback callback) { SpannableString initialContent; @@ -161,20 +167,8 @@ public class SpannableHelper { } SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); - if (emojiList != null && emojiList.size() > 0) { - for (Emoji emoji : emojiList) { - Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) - .matcher(content); - while (matcher.find()) { - CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); - content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); - Glide.with(view) - .asDrawable() - .load(animate ? emoji.url : emoji.static_url) - .into(customEmoji.getTarget(animate)); - } - } - } + CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); + content = customEmoji.makeEmoji(content, emojiList, animate, callback); if (imagesToReplace.size() > 0) { for (Map.Entry entry : imagesToReplace.entrySet()) { @@ -183,12 +177,11 @@ public class SpannableHelper { Matcher matcher = Pattern.compile(key, Pattern.LITERAL) .matcher(content); while (matcher.find()) { - CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); content.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(view) .asDrawable() .load(url) - .into(customEmoji.getTarget(animate)); + .into(customEmoji.getTarget(animate, null)); } } @@ -1066,7 +1059,7 @@ public class SpannableHelper { Glide.with(viewWeakReference.get()) .asDrawable() .load(animate ? emoji.url : emoji.static_url) - .into(customEmoji.getTarget(animate)); + .into(customEmoji.getTarget(animate, null)); } } } diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java index 32326720..d01e12ac 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/ComposeAdapter.java @@ -1107,7 +1107,7 @@ public class ComposeAdapter extends RecyclerView.Adapter(holder.binding.statusContent)), + new WeakReference<>(holder.binding.statusContent), null), TextView.BufferType.SPANNABLE); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); if (status.account != null) { @@ -1122,7 +1122,7 @@ public class ComposeAdapter extends RecyclerView.Adapter(holder.binding.spoiler)), + new WeakReference<>(holder.binding.spoiler), null), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java index ed5c8b61..f163d4b9 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/ConversationAdapter.java @@ -151,7 +151,7 @@ public class ConversationAdapter extends RecyclerView.Adapter(holder.binding.spoiler)), + new WeakReference<>(holder.binding.spoiler), () -> notifyItemChanged(holder.getBindingAdapterPosition())), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); @@ -161,7 +161,7 @@ public class ConversationAdapter extends RecyclerView.Adapter(holder.binding.statusContent)), + new WeakReference<>(holder.binding.statusContent), () -> notifyItemChanged(holder.getBindingAdapterPosition())), TextView.BufferType.SPANNABLE); //--- DATE --- holder.binding.lastMessageDate.setText(Helper.dateDiff(context, conversation.last_status.created_at)); diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java index 78ed4255..4f0ac429 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/NotificationAdapter.java @@ -71,6 +71,7 @@ public class NotificationAdapter extends RecyclerView.Adapter notificationList) { this.notificationList = notificationList; @@ -120,6 +121,13 @@ public class NotificationAdapter extends RecyclerView.Adapter public FetchMoreCallBack fetchMoreCallBack; private Context context; + private RecyclerView mRecyclerView; + public StatusAdapter(List statuses, Timeline.TimeLineEnum timelineType, boolean minified, boolean canBeFederated, boolean checkRemotely) { this.statusList = statuses; this.timelineType = timelineType; @@ -355,6 +357,7 @@ public class StatusAdapter extends RecyclerView.Adapter StatusesVM statusesVM, SearchVM searchVM, StatusViewHolder holder, + RecyclerView recyclerView, RecyclerView.Adapter adapter, List statusList, Status status, @@ -986,7 +989,9 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText( statusToDeal.getSpanSpoiler(context, - new WeakReference<>(holder.binding.spoiler)), + new WeakReference<>(holder.binding.spoiler), () -> { + recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition())); + }), TextView.BufferType.SPANNABLE); statusToDeal.isExpended = true; statusToDeal.isMediaDisplayed = true; @@ -1001,7 +1006,9 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.spoiler.setText( statusToDeal.getSpanSpoiler(context, - new WeakReference<>(holder.binding.spoiler)), + new WeakReference<>(holder.binding.spoiler), () -> { + recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition())); + }), TextView.BufferType.SPANNABLE); } if (statusToDeal.isExpended) { @@ -1049,7 +1056,9 @@ public class StatusAdapter extends RecyclerView.Adapter //--- MAIN CONTENT --- holder.binding.statusContent.setText( statusToDeal.getSpanContent(context, - new WeakReference<>(holder.binding.statusContent)), + new WeakReference<>(holder.binding.statusContent), () -> { + recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition())); + }), TextView.BufferType.SPANNABLE); if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); @@ -1085,7 +1094,9 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.containerTrans.setVisibility(View.VISIBLE); holder.binding.statusContentTranslated.setText( statusToDeal.getSpanTranslate(context, - new WeakReference<>(holder.binding.statusContentTranslated)), + new WeakReference<>(holder.binding.statusContentTranslated), () -> { + recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition())); + }), TextView.BufferType.SPANNABLE); } else { holder.binding.containerTrans.setVisibility(View.GONE); @@ -2087,6 +2098,13 @@ public class StatusAdapter extends RecyclerView.Adapter } + @Override + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + + mRecyclerView = recyclerView; + } + private static boolean mediaObfuscated(Status status) { //Media is not sensitive and doesn't have a spoiler text if (!status.isMediaObfuscated) { @@ -2200,7 +2218,7 @@ public class StatusAdapter extends RecyclerView.Adapter StatusViewHolder holder = (StatusViewHolder) viewHolder; StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class); SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class); - statusManagement(context, statusesVM, searchVM, holder, this, statusList, status, timelineType, minified, canBeFederated, checkRemotely, fetchMoreCallBack); + statusManagement(context, statusesVM, searchVM, holder, mRecyclerView, this, statusList, status, timelineType, minified, canBeFederated, checkRemotely, fetchMoreCallBack); } else if (viewHolder.getItemViewType() == STATUS_FILTERED_HIDE) { StatusViewHolder holder = (StatusViewHolder) viewHolder; diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/StatusHistoryAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/StatusHistoryAdapter.java index 66c2504c..33dd4c86 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/StatusHistoryAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/StatusHistoryAdapter.java @@ -56,13 +56,13 @@ public class StatusHistoryAdapter extends RecyclerView.Adapter(holder.binding.statusContent)), + new WeakReference<>(holder.binding.statusContent), null), TextView.BufferType.SPANNABLE); if (status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) { holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText( status.getSpanSpoiler(context, - new WeakReference<>(holder.binding.spoiler)), + new WeakReference<>(holder.binding.spoiler), null), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); From 83638e9a923c020e8d389b6623b3c29fa487ee5d Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 5 Dec 2022 17:34:37 +0100 Subject: [PATCH 06/31] Beta release 3.9.5 --- app/build.gradle | 4 ++-- app/src/main/assets/release_notes/notes.json | 5 +++++ .../main/java/app/fedilab/android/helper/CustomEmoji.java | 2 +- src/fdroid/fastlane/metadata/android/en/changelogs/440.txt | 4 ++++ 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 src/fdroid/fastlane/metadata/android/en/changelogs/440.txt diff --git a/app/build.gradle b/app/build.gradle index 4ab8912d..acc1514f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { defaultConfig { minSdk 21 targetSdk 32 - versionCode 439 - versionName "3.9.4" + versionCode 440 + versionName "3.9.5" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions "default" diff --git a/app/src/main/assets/release_notes/notes.json b/app/src/main/assets/release_notes/notes.json index 3a01fdff..91c767e8 100644 --- a/app/src/main/assets/release_notes/notes.json +++ b/app/src/main/assets/release_notes/notes.json @@ -1,4 +1,9 @@ [ + { + "version": "3.9.5", + "code": "440", + "note": "Fixed:\n- Custom emoji are not always displayed\n- Jumps in timeline when using \"fit preview images\"\n- Dark theme: timeline buttons without toggle" + }, { "version": "3.9.4", "code": "439", diff --git a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java index 39689d82..f121afc1 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -125,7 +125,7 @@ public class CustomEmoji extends ReplacementSpan { if (view != null) { view.invalidate(); } - if (callback != null) { + if (callback != null && !callbackCalled) { callbackCalled = true; callback.emojiFetched(); } diff --git a/src/fdroid/fastlane/metadata/android/en/changelogs/440.txt b/src/fdroid/fastlane/metadata/android/en/changelogs/440.txt new file mode 100644 index 00000000..f896f566 --- /dev/null +++ b/src/fdroid/fastlane/metadata/android/en/changelogs/440.txt @@ -0,0 +1,4 @@ +Fixed: +- Custom emoji are not always displayed +- Jumps in timeline when using "fit preview images" +- Dark theme: timeline buttons without toggle \ No newline at end of file From 03d6c7f9113257aa65b228af00aef85b440f9067 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 6 Dec 2022 15:59:31 +0100 Subject: [PATCH 07/31] Fix issue #604 - Jumps when scrolling up with media --- .../client/entities/api/Attachment.java | 2 + .../android/ui/drawer/StatusAdapter.java | 439 +++++++----------- 2 files changed, 182 insertions(+), 259 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java b/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java index c4a2b9ab..f569a242 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Attachment.java @@ -51,6 +51,8 @@ public class Attachment implements Serializable { public String focus = null; public String translation = null; + public float measuredWidth = -1.f; + public static class Meta implements Serializable { @SerializedName("focus") public Focus focus; diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java index 57b92e6b..330aede1 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java @@ -375,14 +375,12 @@ public class StatusAdapter extends RecyclerView.Adapter boolean expand_cw = sharedpreferences.getBoolean(context.getString(R.string.SET_EXPAND_CW), false); - boolean expand_media = sharedpreferences.getBoolean(context.getString(R.string.SET_EXPAND_MEDIA), false); boolean display_card = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_CARD), false); boolean share_details = sharedpreferences.getBoolean(context.getString(R.string.SET_SHARE_DETAILS), true); boolean confirmFav = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION_FAV), false); boolean confirmBoost = sharedpreferences.getBoolean(context.getString(R.string.SET_NOTIF_VALIDATION), true); boolean fullAttachement = sharedpreferences.getBoolean(context.getString(R.string.SET_FULL_PREVIEW), false); boolean displayBookmark = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_BOOKMARK), true); - boolean long_press_media = sharedpreferences.getBoolean(context.getString(R.string.SET_LONG_PRESS_STORE_MEDIA), false); boolean displayCounters = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_COUNTER_FAV_BOOST), false); String loadMediaType = sharedpreferences.getString(context.getString(R.string.SET_LOAD_MEDIA_TYPE), "ALWAYS"); @@ -989,9 +987,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText( statusToDeal.getSpanSpoiler(context, - new WeakReference<>(holder.binding.spoiler), () -> { - recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition())); - }), + new WeakReference<>(holder.binding.spoiler), () -> recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition()))), TextView.BufferType.SPANNABLE); statusToDeal.isExpended = true; statusToDeal.isMediaDisplayed = true; @@ -1006,9 +1002,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.spoiler.setText( statusToDeal.getSpanSpoiler(context, - new WeakReference<>(holder.binding.spoiler), () -> { - recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition())); - }), + new WeakReference<>(holder.binding.spoiler), () -> recyclerView.post(() -> adapter.notifyItemChanged(holder.getBindingAdapterPosition()))), TextView.BufferType.SPANNABLE); } if (statusToDeal.isExpended) { @@ -1117,276 +1111,69 @@ public class StatusAdapter extends RecyclerView.Adapter if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) { holder.binding.attachmentsList.removeAllViews(); holder.binding.mediaContainer.removeAllViews(); - //If only one attachment - if (statusToDeal.media_attachments.size() == 1) { - if ((loadMediaType.equals("ASK") || (loadMediaType.equals("WIFI") && !TimelineHelper.isOnWIFI(context))) && !statusToDeal.canLoadMedia) { - holder.binding.mediaContainer.setVisibility(View.GONE); - holder.binding.displayMedia.setVisibility(View.VISIBLE); - holder.binding.displayMedia.setOnClickListener(v -> { - statusToDeal.canLoadMedia = true; - adapter.notifyItemChanged(holder.getBindingAdapterPosition()); - }); - } else { - holder.binding.mediaContainer.setVisibility(View.VISIBLE); - holder.binding.displayMedia.setVisibility(View.GONE); - LayoutMediaBinding layoutMediaBinding = LayoutMediaBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false); - + if ((loadMediaType.equals("ASK") || (loadMediaType.equals("WIFI") && !TimelineHelper.isOnWIFI(context))) && !statusToDeal.canLoadMedia) { + holder.binding.mediaContainer.setVisibility(View.GONE); + holder.binding.displayMedia.setVisibility(View.VISIBLE); + holder.binding.displayMedia.setOnClickListener(v -> { + statusToDeal.canLoadMedia = true; + adapter.notifyItemChanged(holder.getBindingAdapterPosition()); + }); + } else { + int mediaPosition = 1; + boolean singleMedia = statusToDeal.media_attachments.size() == 1; + for (Attachment attachment : statusToDeal.media_attachments) { + LayoutMediaBinding layoutMediaBinding = LayoutMediaBinding.inflate(LayoutInflater.from(context)); if (fullAttachement) { - layoutMediaBinding.media.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - LinearLayout.LayoutParams lp; - layoutMediaBinding.media.getViewTreeObserver().removeOnGlobalLayoutListener(this); - if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.small != null) { - float viewWidth = layoutMediaBinding.media.getWidth(); - float mediaH = statusToDeal.media_attachments.get(0).meta.small.height; - float mediaW = statusToDeal.media_attachments.get(0).meta.small.width; - float ratio = 1.0f; - if (mediaW != 0) { - ratio = viewWidth / mediaW; - } - lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (mediaH * ratio)); - } else { - lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + float ratio = 1.0f; + float mediaH = -1.0f; + + if (attachment.measuredWidth > 0) { + if (attachment.meta != null && attachment.meta.small != null) { + float viewWidth = attachment.measuredWidth; + mediaH = attachment.meta.small.height; + float mediaW = attachment.meta.small.width; + if (mediaW != 0) { + ratio = viewWidth / mediaW; } - layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - layoutMediaBinding.media.setLayoutParams(lp); } - }); - } else { - LinearLayout.LayoutParams lp; - lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) Helper.convertDpToPixel(200, context)); - layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_CROP); - layoutMediaBinding.media.setLayoutParams(lp); - } - if (statusToDeal.sensitive) { - Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, ThemeHelper.getAttColor(context, R.attr.colorError)); - } else { - Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, R.color.white); - } - - layoutMediaBinding.media.setOnClickListener(v -> { - if (statusToDeal.isMediaObfuscated && mediaObfuscated(statusToDeal) && !expand_media) { - statusToDeal.isMediaObfuscated = false; - int position = holder.getBindingAdapterPosition(); - adapter.notifyItemChanged(position); - final int timeout = sharedpreferences.getInt(context.getString(R.string.SET_NSFW_TIMEOUT), 5); - - if (timeout > 0) { - new CountDownTimer((timeout * 1000L), 1000) { - public void onTick(long millisUntilFinished) { - } - - public void onFinish() { - status.isMediaObfuscated = true; - adapter.notifyItemChanged(position); - } - }.start(); - } - return; - } - Intent mediaIntent = new Intent(context, MediaActivity.class); - Bundle b = new Bundle(); - b.putInt(Helper.ARG_MEDIA_POSITION, 1); - b.putSerializable(Helper.ARG_MEDIA_ARRAY, new ArrayList<>(statusToDeal.media_attachments)); - mediaIntent.putExtras(b); - ActivityOptionsCompat options = ActivityOptionsCompat - .makeSceneTransitionAnimation((Activity) context, layoutMediaBinding.media, statusToDeal.media_attachments.get(0).url); - // start the new activity - context.startActivity(mediaIntent, options.toBundle()); - }); - if (statusToDeal.media_attachments.get(0).type != null && (statusToDeal.media_attachments.get(0).type.equalsIgnoreCase("video") || statusToDeal.media_attachments.get(0).type.equalsIgnoreCase("gifv"))) { - layoutMediaBinding.playVideo.setVisibility(View.VISIBLE); - } else { - layoutMediaBinding.playVideo.setVisibility(View.GONE); - } - if (statusToDeal.media_attachments.get(0).type != null && statusToDeal.media_attachments.get(0).type.equalsIgnoreCase("audio")) { - layoutMediaBinding.playMusic.setVisibility(View.VISIBLE); - } else { - layoutMediaBinding.playMusic.setVisibility(View.GONE); - } - String finalUrl; - if (statusToDeal.media_attachments.get(0).url == null) { - finalUrl = statusToDeal.media_attachments.get(0).remote_url; - } else { - finalUrl = statusToDeal.media_attachments.get(0).url; - } - layoutMediaBinding.media.setOnLongClickListener(v -> { - if (long_press_media) { - MediaHelper.manageMove(context, finalUrl, false); - } - return true; - }); - - float focusX = 0.f; - float focusY = 0.f; - if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.focus != null) { - focusX = statusToDeal.media_attachments.get(0).meta.focus.x; - focusY = statusToDeal.media_attachments.get(0).meta.focus.y; - } - if (statusToDeal.media_attachments.get(0).description != null && !statusToDeal.media_attachments.get(0).description.isEmpty()) { - layoutMediaBinding.viewDescription.setVisibility(View.VISIBLE); - } else { - layoutMediaBinding.viewDescription.setVisibility(View.GONE); - } - if (!mediaObfuscated(statusToDeal) || expand_media) { - layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24); - RequestBuilder requestBuilder = Glide.with(layoutMediaBinding.media.getContext()) - .load(statusToDeal.media_attachments.get(0).preview_url); - if (!fullAttachement) { - requestBuilder = requestBuilder.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))); - } - requestBuilder.into(layoutMediaBinding.media); - } else { - layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24); - Glide.with(layoutMediaBinding.media.getContext()) - .load(statusToDeal.media_attachments.get(0).preview_url) - .apply(new RequestOptions().transform(new BlurTransformation(50, 3))) - // .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) - .into(layoutMediaBinding.media); - } - layoutMediaBinding.viewHide.setOnClickListener(v -> { - statusToDeal.sensitive = !statusToDeal.sensitive; - adapter.notifyItemChanged(holder.getBindingAdapterPosition()); - }); - holder.binding.mediaContainer.addView(layoutMediaBinding.getRoot()); - holder.binding.mediaContainer.setVisibility(View.VISIBLE); - holder.binding.attachmentsListContainer.setVisibility(View.GONE); - } - } else { //Several media - if ((loadMediaType.equals("ASK") || (loadMediaType.equals("WIFI") && !TimelineHelper.isOnWIFI(context))) && !statusToDeal.canLoadMedia) { - holder.binding.mediaContainer.setVisibility(View.GONE); - holder.binding.displayMedia.setVisibility(View.VISIBLE); - holder.binding.displayMedia.setOnClickListener(v -> { - statusToDeal.canLoadMedia = true; - adapter.notifyItemChanged(holder.getBindingAdapterPosition()); - }); - } else { - int mediaPosition = 1; - for (Attachment attachment : statusToDeal.media_attachments) { - LayoutMediaBinding layoutMediaBinding = LayoutMediaBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false); - - float focusX = 0.f; - float focusY = 0.f; - if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.focus != null) { - focusX = statusToDeal.media_attachments.get(0).meta.focus.x; - focusY = statusToDeal.media_attachments.get(0).meta.focus.y; - } - layoutMediaBinding.count.setVisibility(View.VISIBLE); - if (!fullAttachement) { - layoutMediaBinding.count.setText(String.format(Locale.getDefault(), "%d/%d", mediaPosition, statusToDeal.media_attachments.size())); - } - String finalUrl; - if (attachment.url == null) { - finalUrl = attachment.remote_url; + loadAndAddAttachment(context, layoutMediaBinding, holder, adapter, mediaPosition, mediaH, ratio, statusToDeal, attachment, singleMedia); } else { - finalUrl = attachment.url; - } - layoutMediaBinding.media.setOnLongClickListener(v -> { - if (long_press_media) { - MediaHelper.manageMove(context, finalUrl, false); - } - return true; - }); - if (fullAttachement) { + int finalMediaPosition = mediaPosition; layoutMediaBinding.media.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { - LinearLayout.LayoutParams lp; layoutMediaBinding.media.getViewTreeObserver().removeOnGlobalLayoutListener(this); + attachment.measuredWidth = layoutMediaBinding.media.getWidth(); + float ratio = 1.0f; + float mediaH = -1.0f; if (attachment.meta != null && attachment.meta.small != null) { - float viewWidth = layoutMediaBinding.media.getWidth(); - float mediaH = attachment.meta.small.height; + float viewWidth = attachment.measuredWidth; + mediaH = attachment.meta.small.height; float mediaW = attachment.meta.small.width; - float ratio = 1.0f; if (mediaW != 0) { ratio = viewWidth / mediaW; } - lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (mediaH * ratio)); - } else { - lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); } - layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - layoutMediaBinding.media.setLayoutParams(lp); + loadAndAddAttachment(context, layoutMediaBinding, holder, adapter, finalMediaPosition, mediaH, ratio, statusToDeal, attachment, singleMedia); } }); - - } else { - LinearLayout.LayoutParams lp; - lp = new LinearLayout.LayoutParams((int) Helper.convertDpToPixel(200, context), (int) Helper.convertDpToPixel(200, context)); - layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_CROP); - layoutMediaBinding.media.setLayoutParams(lp); } - if (attachment.type != null && (attachment.type.equalsIgnoreCase("video") || attachment.type.equalsIgnoreCase("gifv"))) { - layoutMediaBinding.playVideo.setVisibility(View.VISIBLE); - } else { - layoutMediaBinding.playVideo.setVisibility(View.GONE); - } - if (attachment.type != null && attachment.type.equalsIgnoreCase("audio")) { - layoutMediaBinding.playMusic.setVisibility(View.VISIBLE); - } else { - layoutMediaBinding.playMusic.setVisibility(View.GONE); - } - if (attachment.description != null && !attachment.description.isEmpty()) { - layoutMediaBinding.viewDescription.setVisibility(View.VISIBLE); - } else { - layoutMediaBinding.viewDescription.setVisibility(View.GONE); - } - if (!mediaObfuscated(statusToDeal) || expand_media) { - layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24); - RequestBuilder requestBuilder = Glide.with(layoutMediaBinding.media.getContext()) - .load(attachment.preview_url); - if (!fullAttachement) { - requestBuilder = requestBuilder.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))); - } - requestBuilder.into(layoutMediaBinding.media); - } else { - layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24); - Glide.with(layoutMediaBinding.media.getContext()) - .load(attachment.preview_url) - .apply(new RequestOptions().transform(new BlurTransformation(50, 3))) - // .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) - .into(layoutMediaBinding.media); - } - if (statusToDeal.sensitive) { - Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, ThemeHelper.getAttColor(context, R.attr.colorError)); - } else { - Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, R.color.white); - } - - int finalMediaPosition = mediaPosition; - layoutMediaBinding.media.setOnClickListener(v -> { - Intent mediaIntent = new Intent(context, MediaActivity.class); - Bundle b = new Bundle(); - b.putInt(Helper.ARG_MEDIA_POSITION, finalMediaPosition); - b.putSerializable(Helper.ARG_MEDIA_ARRAY, new ArrayList<>(statusToDeal.media_attachments)); - mediaIntent.putExtras(b); - ActivityOptionsCompat options = ActivityOptionsCompat - .makeSceneTransitionAnimation((Activity) context, layoutMediaBinding.media, statusToDeal.media_attachments.get(finalMediaPosition - 1).url); - // start the new activity - context.startActivity(mediaIntent, options.toBundle()); - }); - layoutMediaBinding.viewHide.setOnClickListener(v -> { - statusToDeal.sensitive = !statusToDeal.sensitive; - adapter.notifyItemChanged(holder.getBindingAdapterPosition()); - }); - if (fullAttachement) { - layoutMediaBinding.getRoot().setPadding(0, 0, 0, 10); - holder.binding.mediaContainer.addView(layoutMediaBinding.getRoot()); - } else { - layoutMediaBinding.getRoot().setPadding(0, 0, 10, 0); - holder.binding.attachmentsList.addView(layoutMediaBinding.getRoot()); - } - - mediaPosition++; - } - if (!fullAttachement) { - holder.binding.mediaContainer.setVisibility(View.GONE); - holder.binding.attachmentsListContainer.setVisibility(View.VISIBLE); } else { - holder.binding.mediaContainer.setVisibility(View.VISIBLE); - holder.binding.attachmentsListContainer.setVisibility(View.GONE); + loadAndAddAttachment(context, layoutMediaBinding, holder, adapter, mediaPosition, -1.f, -1.f, statusToDeal, attachment, singleMedia); } + mediaPosition++; + if (fullAttachement || singleMedia) { + holder.binding.mediaContainer.addView(layoutMediaBinding.getRoot()); + } else { + holder.binding.attachmentsList.addView(layoutMediaBinding.getRoot()); + } + } + if (!fullAttachement && !singleMedia) { + holder.binding.mediaContainer.setVisibility(View.GONE); + holder.binding.attachmentsListContainer.setVisibility(View.VISIBLE); + } else { + holder.binding.mediaContainer.setVisibility(View.VISIBLE); + holder.binding.attachmentsListContainer.setVisibility(View.GONE); } } } else { @@ -2098,6 +1885,140 @@ public class StatusAdapter extends RecyclerView.Adapter } + + private static void loadAndAddAttachment(Context context, LayoutMediaBinding layoutMediaBinding, + StatusViewHolder holder, + RecyclerView.Adapter adapter, + int mediaPosition, float mediaH, float ratio, + Status statusToDeal, Attachment attachment, boolean singleImage) { + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); + final int timeout = sharedpreferences.getInt(context.getString(R.string.SET_NSFW_TIMEOUT), 5); + boolean fullAttachement = sharedpreferences.getBoolean(context.getString(R.string.SET_FULL_PREVIEW), false); + boolean long_press_media = sharedpreferences.getBoolean(context.getString(R.string.SET_LONG_PRESS_STORE_MEDIA), false); + boolean expand_media = sharedpreferences.getBoolean(context.getString(R.string.SET_EXPAND_MEDIA), false); + + LinearLayout.LayoutParams lp; + if (fullAttachement && mediaH > 0) { + lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) (mediaH * ratio)); + layoutMediaBinding.media.setScaleType(ImageView.ScaleType.FIT_CENTER); + } else { + if (singleImage) { + lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, (int) Helper.convertDpToPixel(200, context)); + } else { + lp = new LinearLayout.LayoutParams((int) Helper.convertDpToPixel(200, context), (int) Helper.convertDpToPixel(200, context)); + } + layoutMediaBinding.media.setScaleType(ImageView.ScaleType.CENTER_CROP); + } + + + layoutMediaBinding.media.setLayoutParams(lp); + + float focusX = 0.f; + float focusY = 0.f; + if (statusToDeal.media_attachments.get(0).meta != null && statusToDeal.media_attachments.get(0).meta.focus != null) { + focusX = statusToDeal.media_attachments.get(0).meta.focus.x; + focusY = statusToDeal.media_attachments.get(0).meta.focus.y; + } + layoutMediaBinding.count.setVisibility(View.VISIBLE); + if (!fullAttachement && !singleImage) { + layoutMediaBinding.count.setText(String.format(Locale.getDefault(), "%d/%d", mediaPosition, statusToDeal.media_attachments.size())); + } + String finalUrl; + if (attachment.url == null) { + finalUrl = attachment.remote_url; + } else { + finalUrl = attachment.url; + } + layoutMediaBinding.media.setOnLongClickListener(v -> { + if (long_press_media) { + MediaHelper.manageMove(context, finalUrl, false); + } + return true; + }); + + if (attachment.type != null && (attachment.type.equalsIgnoreCase("video") || attachment.type.equalsIgnoreCase("gifv"))) { + layoutMediaBinding.playVideo.setVisibility(View.VISIBLE); + } else { + layoutMediaBinding.playVideo.setVisibility(View.GONE); + } + if (attachment.type != null && attachment.type.equalsIgnoreCase("audio")) { + layoutMediaBinding.playMusic.setVisibility(View.VISIBLE); + } else { + layoutMediaBinding.playMusic.setVisibility(View.GONE); + } + if (attachment.description != null && !attachment.description.isEmpty()) { + layoutMediaBinding.viewDescription.setVisibility(View.VISIBLE); + } else { + layoutMediaBinding.viewDescription.setVisibility(View.GONE); + } + + if (!mediaObfuscated(statusToDeal) || expand_media) { + layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24); + RequestBuilder requestBuilder = Glide.with(layoutMediaBinding.media.getContext()) + .load(attachment.preview_url); + if (!fullAttachement) { + requestBuilder = requestBuilder.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))); + } else { + requestBuilder = requestBuilder.placeholder(R.color.transparent_grey); + requestBuilder = requestBuilder.fitCenter(); + } + requestBuilder.into(layoutMediaBinding.media); + } else { + layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24); + Glide.with(layoutMediaBinding.media.getContext()) + .load(attachment.preview_url) + .apply(new RequestOptions().transform(new BlurTransformation(50, 3))) + // .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) + .into(layoutMediaBinding.media); + } + if (statusToDeal.sensitive) { + Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, ThemeHelper.getAttColor(context, R.attr.colorError)); + } else { + Helper.changeDrawableColor(context, layoutMediaBinding.viewHide, R.color.white); + } + + layoutMediaBinding.media.setOnClickListener(v -> { + if (statusToDeal.isMediaObfuscated && mediaObfuscated(statusToDeal) && !expand_media) { + statusToDeal.isMediaObfuscated = false; + int position = holder.getBindingAdapterPosition(); + adapter.notifyItemChanged(position); + + if (timeout > 0) { + new CountDownTimer((timeout * 1000L), 1000) { + public void onTick(long millisUntilFinished) { + } + + public void onFinish() { + statusToDeal.isMediaObfuscated = true; + adapter.notifyItemChanged(position); + } + }.start(); + } + return; + } + Intent mediaIntent = new Intent(context, MediaActivity.class); + Bundle b = new Bundle(); + b.putInt(Helper.ARG_MEDIA_POSITION, mediaPosition); + b.putSerializable(Helper.ARG_MEDIA_ARRAY, new ArrayList<>(statusToDeal.media_attachments)); + mediaIntent.putExtras(b); + ActivityOptionsCompat options = ActivityOptionsCompat + .makeSceneTransitionAnimation((Activity) context, layoutMediaBinding.media, statusToDeal.media_attachments.get(0).url); + // start the new activity + context.startActivity(mediaIntent, options.toBundle()); + }); + layoutMediaBinding.viewHide.setOnClickListener(v -> { + statusToDeal.sensitive = !statusToDeal.sensitive; + adapter.notifyItemChanged(holder.getBindingAdapterPosition()); + }); + + if (fullAttachement || singleImage) { + layoutMediaBinding.getRoot().setPadding(0, 0, 0, 10); + } else { + layoutMediaBinding.getRoot().setPadding(0, 0, 10, 0); + } + + } + @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); From 7ccb1c96b2ad7d58c878ba53e098abd76556fc27 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 6 Dec 2022 17:50:25 +0100 Subject: [PATCH 08/31] Fix issue #607 - Fetch more broken --- .../android/ui/drawer/StatusAdapter.java | 41 +++++++++++++++---- .../viewmodel/mastodon/TimelinesVM.java | 7 ++++ app/src/main/res/layout/drawer_status.xml | 22 ++++++---- 3 files changed, 54 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java index 330aede1..3b1de7cc 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java @@ -123,6 +123,7 @@ import app.fedilab.android.client.entities.app.BaseAccount; import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.client.entities.app.StatusDraft; import app.fedilab.android.client.entities.app.Timeline; +import app.fedilab.android.databinding.DrawerFetchMoreBinding; import app.fedilab.android.databinding.DrawerStatusArtBinding; import app.fedilab.android.databinding.DrawerStatusBinding; import app.fedilab.android.databinding.DrawerStatusFilteredBinding; @@ -175,6 +176,18 @@ public class StatusAdapter extends RecyclerView.Adapter this.checkRemotely = checkRemotely; } + public static int getStatusPosition(List timelineStatuses, Status status) { + int position = 0; + if (timelineStatuses != null && status != null) { + for (Status _s : timelineStatuses) { + if (_s.id.compareTo(status.id) == 0) { + return position; + } + position++; + } + } + return -1; + } private static boolean isVisible(Timeline.TimeLineEnum timelineType, Status status) { if (timelineType == Timeline.TimeLineEnum.HOME && !show_boosts && status.reblog != null) { @@ -1853,21 +1866,34 @@ public class StatusAdapter extends RecyclerView.Adapter } if (status.isFetchMore && fetchMoreCallBack != null) { - holder.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.VISIBLE); - holder.binding.layoutFetchMore.fetchMoreMin.setOnClickListener(v -> { + DrawerFetchMoreBinding drawerFetchMoreBinding = DrawerFetchMoreBinding.inflate(LayoutInflater.from(context)); + if (status.positionFetchMore == Status.PositionFetchMore.BOTTOM) { + holder.binding.fetchMoreContainerBottom.setVisibility(View.GONE); + holder.binding.fetchMoreContainerTop.setVisibility(View.VISIBLE); + holder.binding.fetchMoreContainerTop.removeAllViews(); + holder.binding.fetchMoreContainerTop.addView(drawerFetchMoreBinding.getRoot()); + } else { + holder.binding.fetchMoreContainerBottom.setVisibility(View.VISIBLE); + holder.binding.fetchMoreContainerTop.setVisibility(View.GONE); + holder.binding.fetchMoreContainerBottom.removeAllViews(); + holder.binding.fetchMoreContainerBottom.addView(drawerFetchMoreBinding.getRoot()); + } + drawerFetchMoreBinding.fetchMoreMin.setOnClickListener(v -> { status.isFetchMore = false; - adapter.notifyItemChanged(holder.getBindingAdapterPosition()); - if (holder.getBindingAdapterPosition() < statusList.size() - 1) { + int position = holder.getBindingAdapterPosition(); + int position2 = getStatusPosition(statusList, status); + adapter.notifyItemChanged(position); + if (position < statusList.size() - 1) { String fromId; if (status.positionFetchMore == Status.PositionFetchMore.TOP) { - fromId = statusList.get(holder.getBindingAdapterPosition() + 1).id; + fromId = statusList.get(position + 1).id; } else { fromId = status.id; } fetchMoreCallBack.onClickMinId(fromId, status); } }); - holder.binding.layoutFetchMore.fetchMoreMax.setOnClickListener(v -> { + drawerFetchMoreBinding.fetchMoreMax.setOnClickListener(v -> { //We hide the button status.isFetchMore = false; String fromId; @@ -1880,7 +1906,8 @@ public class StatusAdapter extends RecyclerView.Adapter adapter.notifyItemChanged(holder.getBindingAdapterPosition()); }); } else { - holder.binding.layoutFetchMore.fetchMoreContainer.setVisibility(View.GONE); + holder.binding.fetchMoreContainerBottom.setVisibility(View.GONE); + holder.binding.fetchMoreContainerTop.setVisibility(View.GONE); } } diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java index d94d1ad0..dd8e752e 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java @@ -29,6 +29,7 @@ import androidx.lifecycle.MutableLiveData; import androidx.preference.PreferenceManager; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -90,8 +91,14 @@ public class TimelinesVM extends AndroidViewModel { super(application); } + + private static void sortDesc(List statusList) { + Collections.sort(statusList, (obj1, obj2) -> obj2.id.compareToIgnoreCase(obj1.id)); + } + private static void addFetchMore(List statusList, List timelineStatuses, TimelineParams timelineParams) throws DBException { if (statusList != null && statusList.size() > 0 && timelineStatuses != null && timelineStatuses.size() > 0) { + sortDesc(statusList); if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.REFRESH || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.SCROLL_TOP || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.FETCH_NEW) { //When refreshing/scrolling to TOP, if last statuses fetched has a greater id from newest in cache, there is potential hole if (statusList.get(statusList.size() - 1).id.compareToIgnoreCase(timelineStatuses.get(0).id) > 0) { diff --git a/app/src/main/res/layout/drawer_status.xml b/app/src/main/res/layout/drawer_status.xml index 3a741fe3..aa6609ab 100644 --- a/app/src/main/res/layout/drawer_status.xml +++ b/app/src/main/res/layout/drawer_status.xml @@ -25,10 +25,6 @@ android:clipToPadding="false" android:clipChildren="false"> - + + + - + From c0f3b0f86bab5a59a5363b058298c0103ea572a2 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 6 Dec 2022 17:55:51 +0100 Subject: [PATCH 09/31] Release 3.9.6 --- app/build.gradle | 4 ++-- app/src/main/assets/release_notes/notes.json | 5 +++++ src/fdroid/fastlane/metadata/android/en/changelogs/441.txt | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/fdroid/fastlane/metadata/android/en/changelogs/441.txt diff --git a/app/build.gradle b/app/build.gradle index acc1514f..4a686c8e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { defaultConfig { minSdk 21 targetSdk 32 - versionCode 440 - versionName "3.9.5" + versionCode 441 + versionName "3.9.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions "default" diff --git a/app/src/main/assets/release_notes/notes.json b/app/src/main/assets/release_notes/notes.json index 91c767e8..668d396b 100644 --- a/app/src/main/assets/release_notes/notes.json +++ b/app/src/main/assets/release_notes/notes.json @@ -1,4 +1,9 @@ [ + { + "version": "3.9.6", + "code": "441", + "note": "Fixed:\n- Jumps with fit preview images when scrolling up\n- Fetch more button broken with cache" + }, { "version": "3.9.5", "code": "440", diff --git a/src/fdroid/fastlane/metadata/android/en/changelogs/441.txt b/src/fdroid/fastlane/metadata/android/en/changelogs/441.txt new file mode 100644 index 00000000..37def619 --- /dev/null +++ b/src/fdroid/fastlane/metadata/android/en/changelogs/441.txt @@ -0,0 +1,3 @@ +Fixed: +- Jumps with fit preview images when scrolling up +- Fetch more button broken with cache \ No newline at end of file From 6cfe8b7b95079f529364a6eaf83a8c1ff7c91f04 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 6 Dec 2022 17:59:28 +0100 Subject: [PATCH 10/31] Release 3.9.6 --- .../fedilab/android/viewmodel/mastodon/NotificationsVM.java | 6 ++++++ .../app/fedilab/android/viewmodel/mastodon/TimelinesVM.java | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java index 16271d4e..cb9d624c 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/NotificationsVM.java @@ -24,6 +24,7 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -64,8 +65,13 @@ public class NotificationsVM extends AndroidViewModel { super(application); } + private static void sortDesc(List notificationList) { + Collections.sort(notificationList, (obj1, obj2) -> obj2.id.compareToIgnoreCase(obj1.id)); + } + private static void addFetchMoreNotifications(List notificationList, List timelineNotifications, TimelinesVM.TimelineParams timelineParams) throws DBException { if (notificationList != null && notificationList.size() > 0 && timelineNotifications != null && timelineNotifications.size() > 0) { + sortDesc(notificationList); if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.REFRESH || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.SCROLL_TOP || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.FETCH_NEW) { //When refreshing/scrolling to TOP, if last statuses fetched has a greater id from newest in cache, there is potential hole if (notificationList.get(notificationList.size() - 1).id.compareToIgnoreCase(timelineNotifications.get(0).id) > 0) { diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java index dd8e752e..4b98de70 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/TimelinesVM.java @@ -96,6 +96,10 @@ public class TimelinesVM extends AndroidViewModel { Collections.sort(statusList, (obj1, obj2) -> obj2.id.compareToIgnoreCase(obj1.id)); } + private static void sortDescConv(List conversationList) { + Collections.sort(conversationList, (obj1, obj2) -> obj2.id.compareToIgnoreCase(obj1.id)); + } + private static void addFetchMore(List statusList, List timelineStatuses, TimelineParams timelineParams) throws DBException { if (statusList != null && statusList.size() > 0 && timelineStatuses != null && timelineStatuses.size() > 0) { sortDesc(statusList); @@ -121,6 +125,7 @@ public class TimelinesVM extends AndroidViewModel { private static void addFetchMoreConversation(List conversationList, List timelineConversations, TimelineParams timelineParams) throws DBException { if (conversationList != null && conversationList.size() > 0 && timelineConversations != null && timelineConversations.size() > 0) { + sortDescConv(conversationList); if (timelineParams.direction == FragmentMastodonTimeline.DIRECTION.REFRESH || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.SCROLL_TOP || timelineParams.direction == FragmentMastodonTimeline.DIRECTION.FETCH_NEW) { //When refreshing/scrolling to TOP, if last statuses fetched has a greater id from newest in cache, there is potential hole if (conversationList.get(conversationList.size() - 1).id.compareToIgnoreCase(timelineConversations.get(0).id) > 0) { From b6f74cb29742f8a00628eff82f2203219cb09e79 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 7 Dec 2022 12:46:34 +0100 Subject: [PATCH 11/31] Add Dracula theme --- .../android/activities/BaseActivity.java | 9 ++ .../activities/BaseAlertDialogActivity.java | 7 + .../android/activities/BaseBarActivity.java | 7 + .../activities/BaseTransparentActivity.java | 7 + .../app/fedilab/android/helper/Helper.java | 2 + .../android/helper/SpannableHelper.java | 12 ++ app/src/main/res/values-night/themes.xml | 128 +++++++++++++++++- app/src/main/res/values/colors.xml | 12 ++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/themes.xml | 11 +- 10 files changed, 188 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/BaseActivity.java b/app/src/main/java/app/fedilab/android/activities/BaseActivity.java index be483f80..2f2b870f 100644 --- a/app/src/main/java/app/fedilab/android/activities/BaseActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/BaseActivity.java @@ -83,6 +83,10 @@ public class BaseActivity extends AppCompatActivity { setTheme(R.style.BlackAppTheme); currentThemeId = R.style.BlackAppTheme; break; + case "DRACULA": + setTheme(R.style.DraculaAppTheme); + currentThemeId = R.style.DraculaAppTheme; + break; } break; } @@ -113,6 +117,11 @@ public class BaseActivity extends AppCompatActivity { setTheme(R.style.BlackAppTheme); currentThemeId = R.style.BlackAppTheme; break; + case "DRACULA": + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + setTheme(R.style.DraculaAppTheme); + currentThemeId = R.style.DraculaAppTheme; + break; } } super.onCreate(savedInstanceState); diff --git a/app/src/main/java/app/fedilab/android/activities/BaseAlertDialogActivity.java b/app/src/main/java/app/fedilab/android/activities/BaseAlertDialogActivity.java index 220526dc..48b51a25 100644 --- a/app/src/main/java/app/fedilab/android/activities/BaseAlertDialogActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/BaseAlertDialogActivity.java @@ -76,6 +76,9 @@ public class BaseAlertDialogActivity extends AppCompatActivity { case "BLACK": setTheme(R.style.BlackAlertDialog); break; + case "DRACULA": + setTheme(R.style.DraculaAlertDialog); + break; } break; } @@ -101,6 +104,10 @@ public class BaseAlertDialogActivity extends AppCompatActivity { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); setTheme(R.style.BlackAlertDialog); break; + case "DRACULA": + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + setTheme(R.style.DraculaAlertDialog); + break; } } super.onCreate(savedInstanceState); diff --git a/app/src/main/java/app/fedilab/android/activities/BaseBarActivity.java b/app/src/main/java/app/fedilab/android/activities/BaseBarActivity.java index dce37269..e8f0111e 100644 --- a/app/src/main/java/app/fedilab/android/activities/BaseBarActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/BaseBarActivity.java @@ -75,6 +75,9 @@ public class BaseBarActivity extends AppCompatActivity { case "BLACK": setTheme(R.style.BlackAppThemeBar); break; + case "DRACULA": + setTheme(R.style.DraculaAppThemeBar); + break; } break; } @@ -101,6 +104,10 @@ public class BaseBarActivity extends AppCompatActivity { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); setTheme(R.style.BlackAppThemeBar); break; + case "DRACULA": + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + setTheme(R.style.DraculaAppThemeBar); + break; } } super.onCreate(savedInstanceState); diff --git a/app/src/main/java/app/fedilab/android/activities/BaseTransparentActivity.java b/app/src/main/java/app/fedilab/android/activities/BaseTransparentActivity.java index 12f89e80..77f2df73 100644 --- a/app/src/main/java/app/fedilab/android/activities/BaseTransparentActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/BaseTransparentActivity.java @@ -75,6 +75,9 @@ public class BaseTransparentActivity extends AppCompatActivity { case "BLACK": setTheme(R.style.TransparentBlack); break; + case "DRACULA": + setTheme(R.style.TransparentDracula); + break; } break; } @@ -101,6 +104,10 @@ public class BaseTransparentActivity extends AppCompatActivity { AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); setTheme(R.style.TransparentBlack); break; + case "DRACULA": + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + setTheme(R.style.TransparentDracula); + break; } } super.onCreate(savedInstanceState); diff --git a/app/src/main/java/app/fedilab/android/helper/Helper.java b/app/src/main/java/app/fedilab/android/helper/Helper.java index c8af7bef..09ed1de6 100644 --- a/app/src/main/java/app/fedilab/android/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/helper/Helper.java @@ -1918,6 +1918,8 @@ public class Helper { return R.style.SolarizedAlertDialog; } else if (R.style.BlackAppThemeBar == currentThemeId || R.style.BlackAppTheme == currentThemeId) { return R.style.BlackAlertDialog; + } else if (R.style.DraculaAppThemeBar == currentThemeId || R.style.DraculaAppTheme == currentThemeId) { + return R.style.DraculaAlertDialog; } return R.style.AppTheme; } diff --git a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java index af12f71d..6b1fe40c 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -439,6 +439,12 @@ public class SpannableHelper { } + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setColor(ThemeHelper.getAttColor(context, R.attr.linkColor)); + } + }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } } @@ -661,6 +667,7 @@ public class SpannableHelper { public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); + ds.setColor(ThemeHelper.getAttColor(context, R.attr.linkColor)); } }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } @@ -694,6 +701,7 @@ public class SpannableHelper { public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); + ds.setColor(ThemeHelper.getAttColor(context, R.attr.linkColor)); } }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } @@ -730,6 +738,7 @@ public class SpannableHelper { public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); + ds.setColor(ThemeHelper.getAttColor(context, R.attr.linkColor)); } }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } @@ -877,6 +886,7 @@ public class SpannableHelper { public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); + ds.setColor(ThemeHelper.getAttColor(context, R.attr.linkColor)); } }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); @@ -1024,6 +1034,8 @@ public class SpannableHelper { @Override public void updateDrawState(@NonNull TextPaint ds) { super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(ThemeHelper.getAttColor(context, R.attr.linkColor)); } }, startPosition, endPosition, diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 72cf8611..847250a4 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,6 +1,10 @@ + + + + - + + - + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5b63928f..e1873589 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -65,6 +65,18 @@ #000000 + #ff79c6 + #50fa7b + #f8f8f2 + #6272a4 + #ffb86c + #f1fa8c + #8be9fd + #282a36 + #44475a + #bd93f9 + + #FFEA00 #42A5F5 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f1fc79aa..1613ea35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -703,6 +703,7 @@ Solarized (Light) Solarized (Dark) Black + Dracula LIGHT @@ -711,6 +712,7 @@ SOLARIZED_LIGHT SOLARIZED_DARK BLACK + DRACULA diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 0bb81b60..7d4ec92c 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,6 +1,9 @@ + + - +