From a632618a66fdf711e7d2dbda3b500cdeadd91ef4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 10 Jul 2022 10:41:07 +0200 Subject: [PATCH 01/50] Some improvements --- .../android/activities/ContextActivity.java | 2 +- .../android/ui/drawer/StatusAdapter.java | 18 ---------- .../FragmentMastodonNotification.java | 31 +++++++++-------- .../timeline/FragmentMastodonTimeline.java | 34 +++++++++++-------- 4 files changed, 37 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java index b763e87f3..533ba40f0 100644 --- a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java @@ -82,7 +82,7 @@ public class ContextActivity extends BaseActivity { focusedStatus = null; // or other values if (b != null) focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS); - if (focusedStatus == null && currentAccount == null || currentAccount.mastodon_account == null) { + if (focusedStatus == null || currentAccount == null || currentAccount.mastodon_account == null) { finish(); return; } 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 34c970a06..c8aacea37 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 @@ -1787,10 +1787,6 @@ public class StatusAdapter extends RecyclerView.Adapter holder.timer.cancel(); holder.timer = null; } - if (holder.dateTimer != null) { - holder.dateTimer.cancel(); - holder.dateTimer = null; - } if (status.emojis != null && status.emojis.size() > 0) { holder.timer = new Timer(); holder.timer.scheduleAtFixedRate(new TimerTask() { @@ -1803,16 +1799,6 @@ public class StatusAdapter extends RecyclerView.Adapter } }, 100, 100); } - holder.dateTimer = new Timer(); - holder.dateTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> holder.binding.dateShort.setText(Helper.dateDiff(context, status.created_at)); - mainHandler.post(myRunnable); - - } - }, 100, 10000); } else if (viewHolder.getItemViewType() == STATUS_ART) { StatusViewHolder holder = (StatusViewHolder) viewHolder; MastodonHelper.loadPPMastodon(holder.bindingArt.artPp, status.account); @@ -1881,9 +1867,6 @@ public class StatusAdapter extends RecyclerView.Adapter if (holder instanceof StatusViewHolder && ((StatusViewHolder) holder).timer != null) { ((StatusViewHolder) holder).timer.cancel(); } - if (holder instanceof StatusViewHolder && ((StatusViewHolder) holder).dateTimer != null) { - ((StatusViewHolder) holder).dateTimer.cancel(); - } } public interface FetchMoreCallBack { @@ -1900,7 +1883,6 @@ public class StatusAdapter extends RecyclerView.Adapter DrawerStatusNotificationBinding bindingNotification; DrawerStatusArtBinding bindingArt; Timer timer; - Timer dateTimer; StatusViewHolder(DrawerStatusBinding itemView) { super(itemView.getRoot()); 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 70005b28e..bd7a50e46 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 @@ -456,25 +456,28 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } int position = 0; //We loop through messages already in the timeline - for (Notification notificationsAlreadyPresent : this.notificationList) { - //We compare the date of each status and we only add status having a date greater than the another, it is inserted at this position - //Pinned messages are ignored because their date can be older - if (notificationReceived.id.compareTo(notificationsAlreadyPresent.id) > 0) { + if (this.notificationList != null) { + notificationAdapter.notifyItemRangeChanged(0, this.notificationList.size()); + for (Notification notificationsAlreadyPresent : this.notificationList) { + //We compare the date of each status and we only add status having a date greater than the another, it is inserted at this position + //Pinned messages are ignored because their date can be older + if (notificationReceived.id.compareTo(notificationsAlreadyPresent.id) > 0) { + //We add the status to a list of id - thus we know it is already in the timeline + idOfAddedNotifications.add(notificationReceived.id); + this.notificationList.add(position, notificationReceived); + notificationAdapter.notifyItemInserted(position); + break; + } + position++; + } + //Statuses added at the bottom, we flag them by position = -2 for not dealing with them and fetch more + if (position == this.notificationList.size()) { //We add the status to a list of id - thus we know it is already in the timeline idOfAddedNotifications.add(notificationReceived.id); this.notificationList.add(position, notificationReceived); notificationAdapter.notifyItemInserted(position); - break; + return NOTIFICATION__AT_THE_BOTTOM; } - position++; - } - //Statuses added at the bottom, we flag them by position = -2 for not dealing with them and fetch more - if (position == this.notificationList.size()) { - //We add the status to a list of id - thus we know it is already in the timeline - idOfAddedNotifications.add(notificationReceived.id); - this.notificationList.add(position, notificationReceived); - notificationAdapter.notifyItemInserted(position); - return NOTIFICATION__AT_THE_BOTTOM; } return position; } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index 5d3089797..a69d7d38d 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -499,27 +499,31 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. return STATUS_PRESENT; } int position = 0; - //We loop through messages already in the timeline - for (Status statusAlreadyPresent : this.statuses) { - //We compare the date of each status and we only add status having a date greater than the another, it is inserted at this position - //Pinned messages are ignored because their date can be older - if (statusReceived.id.compareTo(statusAlreadyPresent.id) > 0 && !statusAlreadyPresent.pinned) { + if (this.statuses != null) { + statusAdapter.notifyItemRangeChanged(0, this.statuses.size()); + //We loop through messages already in the timeline + for (Status statusAlreadyPresent : this.statuses) { + //We compare the date of each status and we only add status having a date greater than the another, it is inserted at this position + //Pinned messages are ignored because their date can be older + if (statusReceived.id.compareTo(statusAlreadyPresent.id) > 0 && !statusAlreadyPresent.pinned) { + //We add the status to a list of id - thus we know it is already in the timeline + idOfAddedStatuses.add(statusReceived.id); + this.statuses.add(position, statusReceived); + statusAdapter.notifyItemInserted(position); + break; + } + position++; + } + //Statuses added at the bottom, we flag them by position = -2 for not dealing with them and fetch more + if (position == this.statuses.size()) { //We add the status to a list of id - thus we know it is already in the timeline idOfAddedStatuses.add(statusReceived.id); this.statuses.add(position, statusReceived); statusAdapter.notifyItemInserted(position); - break; + return STATUS_AT_THE_BOTTOM; } - position++; - } - //Statuses added at the bottom, we flag them by position = -2 for not dealing with them and fetch more - if (position == this.statuses.size()) { - //We add the status to a list of id - thus we know it is already in the timeline - idOfAddedStatuses.add(statusReceived.id); - this.statuses.add(position, statusReceived); - statusAdapter.notifyItemInserted(position); - return STATUS_AT_THE_BOTTOM; } + return position; } From b158d0131159a6be92b0cdd56295e912f5f93024 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 10 Jul 2022 11:34:41 +0200 Subject: [PATCH 02/50] Allow to reset language --- .../settings/FragmentLanguageSettings.java | 17 ++++++++++++++++- app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/pref_language.xml | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentLanguageSettings.java b/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentLanguageSettings.java index 7e1961227..1f99ccaf9 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentLanguageSettings.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/settings/FragmentLanguageSettings.java @@ -14,11 +14,14 @@ package app.fedilab.android.ui.fragment.settings; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.annotation.SuppressLint; import android.content.SharedPreferences; import android.os.Bundle; import androidx.preference.ListPreference; +import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceManager; import app.fedilab.android.R; import app.fedilab.android.helper.Helper; @@ -31,16 +34,28 @@ public class FragmentLanguageSettings extends PreferenceFragmentCompat implement createPref(); } + @SuppressLint("ApplySharedPref") private void createPref() { ListPreference SET_DEFAULT_LOCALE_NEW = findPreference(getString(R.string.SET_DEFAULT_LOCALE_NEW)); if (SET_DEFAULT_LOCALE_NEW != null) { SET_DEFAULT_LOCALE_NEW.getContext().setTheme(Helper.dialogStyle()); } + + Preference SET_TRANSLATE_VALUES_RESET = findPreference(getString(R.string.SET_TRANSLATE_VALUES_RESET)); + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()); + if (SET_TRANSLATE_VALUES_RESET != null) { + SET_TRANSLATE_VALUES_RESET.setOnPreferenceClickListener(preference -> { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putString(getString(R.string.SET_DEFAULT_LOCALE_NEW), null); + editor.commit(); + return true; + }); + } } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.compareToIgnoreCase(getString(R.string.SET_DEFAULT_LOCALE_NEW)) == 0) { + if (key.compareToIgnoreCase(getString(R.string.SET_DEFAULT_LOCALE_NEW)) == 0 || key.compareToIgnoreCase(getString(R.string.SET_TRANSLATE_VALUES_RESET)) == 0) { requireActivity().recreate(); Helper.recreateMainActivity(requireActivity()); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 776a82e55..c50d34396 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -779,6 +779,7 @@ SET_FEATURED_TAG_ACTION SET_RETRIEVE_METADATA_IF_URL_FROM_EXTERAL + SET_TRANSLATE_VALUES_RESET en fr @@ -978,6 +979,7 @@ File cache size Clear cache Are you sure you want to delete cache? If you have drafts with media, the attached media will be lost. + Use the default system language diff --git a/app/src/main/res/xml/pref_language.xml b/app/src/main/res/xml/pref_language.xml index c15f0f1b5..ae608822c 100644 --- a/app/src/main/res/xml/pref_language.xml +++ b/app/src/main/res/xml/pref_language.xml @@ -11,4 +11,10 @@ app:summary="@string/set_push_notifications" app:title="@string/set_change_locale" app:useSimpleSummaryProvider="true" /> + + \ No newline at end of file From a11633f12774a670b70f5741246abc2a3debd749 Mon Sep 17 00:00:00 2001 From: mastoduy Date: Sun, 10 Jul 2022 07:57:07 +0000 Subject: [PATCH 03/50] Translated using Weblate (Vietnamese) Currently translated at 100.0% (724 of 724 strings) Translation: Fedilab/Strings Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/vi/ --- app/src/main/res/values-vi/strings.xml | 75 +++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 02e77fe24..1a2b48cfc 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -16,7 +16,7 @@ Tập tin: %1$s Mật khẩu Email - Tài khoản + Người Tút Tag Lưu @@ -63,7 +63,7 @@ Ứng dụng không thể thu thập emoji tùy chỉnh vào thời điểm này. Bạn có chắc muốn đăng xuất @%1$s@%2$s\? - Không có tút nào + Chưa có Thêm tút này vào mục yêu thích của bạn\? Xóa tút này khỏi mục yêu thích của bạn\? Đăng lại tút này\? @@ -75,7 +75,7 @@ Sao chép Chia sẻ Nhắc đến - Thời gian ẩn + Ẩn có thời hạn Xóa & viết lại Ẩn tài khoản này? @@ -143,11 +143,14 @@ Tìm máy chủ: - Không có tài khoản nào + Không có người nào Không có yêu cầu theo dõi - %1$s tút - %1$s Đang theo dõi - %1$s Người theo dõi + Tút +\n %1$s + Đang theo dõi +\n %1$s + Người theo dõi +\n %1$s Từ chối Không có tút đã lên lịch! @@ -198,10 +201,10 @@ Khi ai đó đăng lại tút của tôi Khi ai đó thích tút của tôi Khi ai đó nhắc tới tôi - Nhắc khi một cuộc bình chọn kết thúc - Nhắc khi có tút mới - Hiện xác nhận trước khi đăng lại tút - Hiện xác nhận trước khi thích tút + Khi cuộc bình chọn kết thúc + Khi có tút mới + Hiện xác nhận trước khi đăng lại + Hiện xác nhận trước khi thích Thông báo? Tắt thông báo Thời gian chờ của NSFW (giây, 0 = tắt) @@ -237,7 +240,7 @@ Bỏ ẩn Đã gửi yêu cầu Theo dõi bạn - Viết hoa chữ đầu khi trả lời + Tự động xuống dòng khi trả lời Giảm kích cỡ hình ảnh Giảm kích thước video @@ -273,7 +276,7 @@ Thêm vào danh sách Xoá danh sách Tên danh sách mới - Đã thêm tài khoản vào danh sách! + Đã thêm người này vào danh sách! Bạn chưa có danh sách nào! %1$s đã chuyển sang %2$s @@ -325,7 +328,7 @@ Tút mới Tải media Chọn âm thanh - Hiện thời gian đăng + Thời gian thông báo Bạn có chắc bỏ chặn %s\? \n \nBạn sẽ không thấy bất kỳ nội dung nào từ máy chủ này trong bảng tin và thông báo của bạn. Những người theo dõi bạn từ máy chủ này cũng sẽ bị xóa. @@ -405,9 +408,9 @@ Lựa chọn %d Bạn cần cho ít nhất 2 lựa chọn! Xong - kết thúc lúc %s + kết thúc %s Bình chọn - Cuộc bình chọn có bạn tham gia đã kết thúc + Cuộc bình chọn bạn đã tham gia kết thúc Cuộc bình chọn của bạn đã kết thúc Loại Chuyển bảng tin @@ -490,7 +493,7 @@ Ghi âm Ứng dụng sẽ gửi thông báo vào thời điểm chỉ định. Bạn có thể đảo ngược (tức là: im lặng) thời điểm này bằng con quay bên phải. Không cắt ảnh xem trước - Tự động chèn dấu ngắt dòng sau phần đề cập để viết hoa chữ cái đầu tiên + Tự động xuống dòng và viết hoa chữ đầu tiên sau khi nhắc đến ai đó. Chia sẻ tút với nguồn cấp dữ liệu RSS Soạn thảo Chọn @@ -502,8 +505,8 @@ \n \nBạn có thể thêm nội dung. Cảm ơn bạn! Mức độ hiển thị - Tắt emoji dạng GIF - Báo cáo tài khoản + Tắt emoji GIF + Báo cáo người này %d bình chọn @@ -527,10 +530,10 @@ Có lựa chọn trùng lặp! Xóa bộ nhớ đệm khi thoát Bộ nhớ đệm (media, tút, dữ liệu từ trình duyệt tích hợp) sẽ tự động bị xóa khi thoát ứng dụng. - Bạn có muốn ngưng theo dõi tài khoản này\? - Yêu cầu xác nhận trước khi hủy theo dõi ai đó + Bạn có muốn ngưng theo dõi người này\? + Yêu cầu xác nhận trước khi ngưng theo dõi ai đó. Thay thế liên kết từ Medium - Thay thế liên kết từ medium.com với một giao diện mã nguồn mở, chú trọng bảo mật. + Dùng một frontend thay thế cho Medium Mặc định: scribe.rip Sử dụng hệ thống thông báo đẩy để nhận thông báo trong thời gian thực. Thêm ghi chú @@ -615,10 +618,10 @@ Tên danh sách không hợp lệ! Không có người dùng nào trong danh sách này! Đã lên lịch - Trong khoảng thời gian + Trong khoảng thời gian này Theme gốc Chọn theme gốc là tối hay sáng - Tùy chỉnh bảng tin + Tùy chỉnh Theme của cộng đồng Chọn một theme được tạo bởi cộng đồng Hiển thị @@ -644,7 +647,7 @@ Kiểu tút mặc định: Đã thêm tút vào mục yêu thích! Đã xóa tút khỏi mục yêu thích! - Số lượng tài khoản mỗi lần tải + Số lượng người dùng mỗi lần tải Số lượng thông báo mỗi lần tải Âm nhạc Không thể để trống mục này! @@ -665,7 +668,7 @@ Địa chỉ máy chủ không hợp lệ! Đăng lại bởi Thích bởi - Hạn chế + Riêng tư Khác Vd: Nội Dung Nhạy Cảm Thêm trạng thái @@ -688,11 +691,11 @@ Đây là spam Liên kết độc hại, giả tương tác hoặc trả lời lặp đi lặp lại Vi phạm quy tắc máy chủ - Bạn đang theo dõi tài khoản này. Để không nhìn thấy tút của họ trong bảng tin nữa, hãy ngưng theo dõi họ. + Bạn đang theo dõi người này. Để không nhìn thấy tút của họ trong bảng tin nữa, hãy ngưng theo dõi họ. Ẩn %1$s Bạn sẽ không thấy tút của họ. Họ vẫn có thể theo dõi bạn và thấy tút của bạn nhưng không biết rằng họ bị ẩn. Chặn %1$s - Tài khoản này từ một máy chủ khác. Gửi luôn cho kiểm duyệt viên máy chủ đó\? + Người này từ một máy chủ khác. Gửi luôn cho kiểm duyệt viên máy chủ đó\? Chuyển tiếp %1$s Đã gửi báo cáo! Bạn sẽ không thấy tút của họ. Họ sẽ không thể xem tút của bạn hoặc theo dõi bạn. Họ sẽ biếtrằng họ bị chặn. @@ -704,7 +707,7 @@ Kết quả bình chọn Cập nhật từ mọi người Theo dõi - Đánh dấu tất cả là đã đọc + Đánh dấu đã đọc xong Hiện toàn bộ Bạn có chắc muốn xóa tất cả thông báo\? Không thể khôi phục lại. \"Mastodon không phải là một trang web duy nhất như Twitter hoặc Facebook, đó là một mạng lưới hàng ngàn cộng đồng được điều hành bởi các tổ chức và cá nhân khác nhau cung cấp trải nghiệm truyền thông xã hội liền mạch.\" @@ -730,7 +733,7 @@ Gần đây nhất Bộ lọc Tên miền - Nguồn gốc tài khoản bị báo cáo + Máy chủ người bị báo cáo Trạng thái Đã xử lý Đã duyệt @@ -745,10 +748,10 @@ Đường thẳng Chế độ Xóa Xóa cache - Tút trong cache cho Bảng Tin - Tút lưu trong nháp - Bạn có chắc muốn xóa cache\? Nếu bạn có tút nháp đính kèm media, nó sẽ bị mất. - Tút trong cache những Bảng Tin khác - Kích cỡ cache - Xóa cache + Bộ nhớ đệm tút Bảng Tin + Tút nháp + Bạn có chắc muốn xóa bộ nhớ đệm\? Nếu bạn có tút nháp đính kèm media, nó sẽ bị mất. + Bộ nhớ đệm tút những Bảng Tin khác + Dung lượng cho phép + Xóa bộ nhớ đệm \ No newline at end of file From c17e2c4ba40eccff417529c4c56cde6e80018602 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 10 Jul 2022 12:20:18 +0200 Subject: [PATCH 04/50] Fix Nitter Pagination + Allow to share through Nitter --- .../client/entities/nitter/Nitter.java | 11 ++++++- .../android/ui/drawer/StatusAdapter.java | 29 +++++++++++++++++++ .../viewmodel/mastodon/TimelinesVM.java | 5 ++-- app/src/main/res/layout/drawer_status.xml | 21 ++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/client/entities/nitter/Nitter.java b/app/src/main/java/app/fedilab/android/client/entities/nitter/Nitter.java index afc88ee4d..d53985337 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/nitter/Nitter.java +++ b/app/src/main/java/app/fedilab/android/client/entities/nitter/Nitter.java @@ -105,8 +105,8 @@ public class Nitter implements Serializable { } } Nitter nitterAccount = accounts.get(feedItem.creator); + app.fedilab.android.client.entities.api.Account account = new app.fedilab.android.client.entities.api.Account(); if (nitterAccount != null) { - app.fedilab.android.client.entities.api.Account account = new app.fedilab.android.client.entities.api.Account(); String[] names = nitterAccount.image.title.split("/"); account.id = feedItem.guid; account.acct = names[1].replace("@", ""); @@ -116,6 +116,15 @@ public class Nitter implements Serializable { account.avatar_static = nitterAccount.image.url; account.url = nitterAccount.image.link; status.account = account; + } else { + account.id = feedItem.guid; + account.acct = feedItem.creator.replace("@", ""); + account.username = feedItem.creator.replace("@", ""); + account.display_name = feedItem.creator.replace("@", ""); + account.avatar = ""; + account.avatar_static = ""; + account.url = feedItem.link; + status.account = account; } if (feedItem.description != null) { 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 c8aacea37..208055d0c 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 @@ -471,6 +471,35 @@ public class StatusAdapter extends RecyclerView.Adapter } else { holder.binding.card.setVisibility(View.GONE); } + if (!canBeFederated) { + holder.binding.actionShareContainer.setVisibility(View.VISIBLE); + holder.binding.actionShare.setOnClickListener(v -> { + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via)); + String url; + if (statusToDeal.uri.startsWith("http")) + url = status.uri; + else + url = status.url; + String extra_text; + if (share_details) { + extra_text = statusToDeal.account.acct; + if (extra_text.split("@").length == 1) + extra_text = "@" + extra_text + "@" + BaseMainActivity.currentInstance; + else + extra_text = "@" + extra_text; + extra_text += " \uD83D\uDD17 " + url + "\r\n-\n"; + extra_text += statusToDeal.text; + } else { + extra_text = url; + } + sendIntent.putExtra(Intent.EXTRA_TEXT, extra_text); + sendIntent.setType("text/plain"); + context.startActivity(Intent.createChooser(sendIntent, context.getString(R.string.share_with))); + }); + } else { + holder.binding.actionShareContainer.setVisibility(View.GONE); + } if (minified || !canBeFederated) { holder.binding.actionButtons.setVisibility(View.GONE); } else { 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 a04c2a0de..465ec9e59 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 @@ -219,7 +219,6 @@ public class TimelinesVM extends AndroidViewModel { statusesMutableLiveData = new MutableLiveData<>(); new Thread(() -> { Call publicTlCall = mastodonTimelinesService.getNitter(accountsStr, max_position); - Statuses statuses = new Statuses(); if (publicTlCall != null) { try { @@ -234,7 +233,9 @@ public class TimelinesVM extends AndroidViewModel { } } statuses.statuses = SpannableHelper.convertNitterStatus(getApplication().getApplicationContext(), statusList); - statuses.pagination = MastodonHelper.getPagination(publicTlResponse.headers()); + String max_id = publicTlResponse.headers().get("min-id"); + statuses.pagination = new Pagination(); + statuses.pagination.max_id = max_id; } } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/res/layout/drawer_status.xml b/app/src/main/res/layout/drawer_status.xml index 93808fc2a..54e66bf4f 100644 --- a/app/src/main/res/layout/drawer_status.xml +++ b/app/src/main/res/layout/drawer_status.xml @@ -492,6 +492,27 @@ + + + + + Date: Sun, 10 Jul 2022 12:28:09 +0200 Subject: [PATCH 05/50] Some cleaning --- .../android/ui/drawer/StatusAdapter.java | 79 ++++++------------- 1 file changed, 23 insertions(+), 56 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 208055d0c..6761cfa7c 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 @@ -316,9 +316,9 @@ public class StatusAdapter extends RecyclerView.Adapter boolean displayBookmark = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_BOOKMARK), false); int truncate_toots_size = sharedpreferences.getInt(context.getString(R.string.SET_TRUNCATE_TOOTS_SIZE), 0); - boolean display_video_preview = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_VIDEO_PREVIEWS), true); - boolean isModerator = sharedpreferences.getBoolean(Helper.PREF_IS_MODERATOR, false); - boolean isAdmin = sharedpreferences.getBoolean(Helper.PREF_IS_ADMINISTRATOR, false); + // boolean display_video_preview = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_VIDEO_PREVIEWS), true); + // boolean isModerator = sharedpreferences.getBoolean(Helper.PREF_IS_MODERATOR, false); + // boolean isAdmin = sharedpreferences.getBoolean(Helper.PREF_IS_ADMINISTRATOR, false); int theme_icons_color = -1; int theme_statuses_color = -1; int theme_boost_header_color = -1; @@ -525,9 +525,7 @@ public class StatusAdapter extends RecyclerView.Adapter if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = statusList.get(0); statusesVM.bookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, true)); } else { Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); } @@ -535,16 +533,11 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (statusToDeal.bookmarked) { statusesVM.unBookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNBOOKMARK_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNBOOKMARK_ACTION, statusToDeal, _status, false)); } else { ((SparkButton) v).playAnimation(); statusesVM.bookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, remote); - - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, false)); } } }); @@ -633,9 +626,7 @@ public class StatusAdapter extends RecyclerView.Adapter if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id, null) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, true)); } else { Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); } @@ -643,15 +634,11 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (statusToDeal.reblogged) { statusesVM.unReblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, false)); } else { ((SparkButton) v).playAnimation(); statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, null) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, false)); } } dialog.dismiss(); @@ -667,9 +654,7 @@ public class StatusAdapter extends RecyclerView.Adapter if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id, null) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, true)); } else { Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); } @@ -677,15 +662,11 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (statusToDeal.reblogged) { statusesVM.unReblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, false)); } else { ((SparkButton) v).playAnimation(); statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, null) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, false)); } } } @@ -715,9 +696,7 @@ public class StatusAdapter extends RecyclerView.Adapter if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, true)); } else { Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); } @@ -725,15 +704,11 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (status.favourited) { statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, false)); } else { ((SparkButton) v).playAnimation(); statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, false)); } } dialog.dismiss(); @@ -749,9 +724,7 @@ public class StatusAdapter extends RecyclerView.Adapter if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, true)); } else { Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); } @@ -759,15 +732,11 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (statusToDeal.favourited) { statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, false)); } else { ((SparkButton) v).playAnimation(); statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) - .observe((LifecycleOwner) context, _status -> { - manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote); - }); + .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, false)); } } } @@ -1351,9 +1320,7 @@ public class StatusAdapter extends RecyclerView.Adapter return false; }); if (!minified && canBeFederated) { - holder.binding.mainContainer.setOnClickListener(v -> { - holder.binding.statusContent.callOnClick(); - }); + holder.binding.mainContainer.setOnClickListener(v -> holder.binding.statusContent.callOnClick()); holder.binding.statusContent.setOnClickListener(v -> { if (status.isFocused || v.getTag() == SpannableHelper.CLICKABLE_SPAN) { if (v.getTag() == SpannableHelper.CLICKABLE_SPAN) { @@ -1475,11 +1442,11 @@ public class StatusAdapter extends RecyclerView.Adapter builderInner.show(); } else if (itemId == R.id.action_schedule_boost) { MastodonHelper.scheduleBoost(context, MastodonHelper.ScheduleType.BOOST, statusToDeal, null, null); - } else if (itemId == R.id.action_admin) { - /* Intent intent = new Intent(context, AccountReportActivity.class); + } /*else if (itemId == R.id.action_admin) { + Intent intent = new Intent(context, AccountReportActivity.class); intent.putExtra(Helper.ARG_ACCOUNT, statusToDeal.account); - context.startActivity(intent);*/ - } else if (itemId == R.id.action_open_browser) { + context.startActivity(intent); + } */ else if (itemId == R.id.action_open_browser) { Helper.openBrowser(context, statusToDeal.url); } else if (itemId == R.id.action_remove) { AlertDialog.Builder builderInner = new AlertDialog.Builder(context, Helper.dialogStyle()); From 425d19f2d6c5583f5edc013e0114e12d51347286 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 10 Jul 2022 17:45:56 +0200 Subject: [PATCH 06/50] Fix issue #237 - CW not filtered --- .../android/helper/TimelineHelper.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/src/main/java/app/fedilab/android/helper/TimelineHelper.java b/app/src/main/java/app/fedilab/android/helper/TimelineHelper.java index b37f899f4..766f8f18c 100644 --- a/app/src/main/java/app/fedilab/android/helper/TimelineHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/TimelineHelper.java @@ -107,6 +107,18 @@ public class TimelineHelper { Matcher m = p.matcher(content); if (m.find()) { statusesToRemove.add(status); + continue; + } + if (status.spoiler_text != null) { + String spoilerText; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + spoilerText = Html.fromHtml(status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString(); + else + spoilerText = Html.fromHtml(status.spoiler_text).toString(); + Matcher ms = p.matcher(spoilerText); + if (ms.find()) { + statusesToRemove.add(status); + } } } } else { @@ -118,6 +130,18 @@ public class TimelineHelper { content = Html.fromHtml(status.content).toString(); if (content.contains(filter.phrase)) { statusesToRemove.add(status); + continue; + } + + if (status.spoiler_text != null) { + String spoilerText; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + spoilerText = Html.fromHtml(status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString(); + else + spoilerText = Html.fromHtml(status.spoiler_text).toString(); + if (spoilerText.contains(filter.phrase)) { + statusesToRemove.add(status); + } } } } From eb1be4f51492c093efe52a47d5d98207dec5f7a1 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 10 Jul 2022 17:46:14 +0200 Subject: [PATCH 07/50] Some fixes --- .../app/fedilab/android/activities/ContextActivity.java | 6 ------ .../app/fedilab/android/activities/DraftActivity.java | 6 ------ .../app/fedilab/android/activities/MediaActivity.java | 1 - .../app/fedilab/android/activities/SettingsActivity.java | 9 --------- .../fedilab/android/ui/fragment/media/FragmentMedia.java | 5 ++--- .../fragment/timeline/FragmentMastodonNotification.java | 2 +- 6 files changed, 3 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java index 533ba40f0..61732dcfb 100644 --- a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java @@ -168,10 +168,4 @@ public class ContextActivity extends BaseActivity { return true; } - @Override - protected void onDestroy() { - super.onDestroy(); - binding = null; - currentFragment = null; - } } \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/android/activities/DraftActivity.java b/app/src/main/java/app/fedilab/android/activities/DraftActivity.java index cfbb235bf..bcf6585f3 100644 --- a/app/src/main/java/app/fedilab/android/activities/DraftActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/DraftActivity.java @@ -205,12 +205,6 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr } } - @Override - public void onDestroy() { - super.onDestroy(); - binding.lvStatus.setAdapter(null); - binding = null; - } @Override public void onAllDeleted() { diff --git a/app/src/main/java/app/fedilab/android/activities/MediaActivity.java b/app/src/main/java/app/fedilab/android/activities/MediaActivity.java index ca275b439..2fe067f2d 100644 --- a/app/src/main/java/app/fedilab/android/activities/MediaActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/MediaActivity.java @@ -294,7 +294,6 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface { @Override public void onDestroy() { - binding = null; unregisterReceiver(onDownloadComplete); super.onDestroy(); } diff --git a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java index e6502caa8..6bd43a2b3 100644 --- a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java @@ -156,15 +156,6 @@ public class SettingsActivity extends BaseActivity { } - @Override - protected void onDestroy() { - super.onDestroy(); - if (currentFragment != null) { - currentFragment.onDestroy(); - } - binding = null; - - } @Override public boolean onOptionsItemSelected(MenuItem item) { diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMedia.java b/app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMedia.java index 08fee2707..3395cd594 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMedia.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/media/FragmentMedia.java @@ -122,7 +122,7 @@ public class FragmentMedia extends Fragment { binding.mediaPicture.setVisibility(View.VISIBLE); binding.mediaPicture.setTransitionName(attachment.url); - if (Helper.isValidContextForGlide(requireActivity())) { + if (Helper.isValidContextForGlide(requireActivity()) && isAdded()) { Glide.with(requireActivity()) .asBitmap() .dontTransform() @@ -142,7 +142,7 @@ public class FragmentMedia extends Fragment { binding.mediaPicture.setVisibility(View.VISIBLE); binding.pbarInf.setIndeterminate(true); binding.loader.setVisibility(View.VISIBLE); - if (Helper.isValidContextForGlide(requireActivity())) { + if (Helper.isValidContextForGlide(requireActivity()) && isAdded()) { Glide.with(requireActivity()) .asBitmap() .dontTransform() @@ -312,7 +312,6 @@ public class FragmentMedia extends Fragment { timer.cancel(); timer = null; } - binding = null; } @Override 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 bd7a50e46..e17d319aa 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 @@ -494,8 +494,8 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } }).start(); } - super.onDestroyView(); LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action); + super.onDestroyView(); } @Override From 993ecc2382e21b4ec82e0c64154d3af4615f1023 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 10 Jul 2022 18:17:40 +0200 Subject: [PATCH 08/50] Fix issue #238 --- .../android/ui/drawer/EmojiAdapter.java | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java index 785c38368..9f849b1f6 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/EmojiAdapter.java @@ -16,26 +16,16 @@ package app.fedilab.android.ui.drawer; import android.content.SharedPreferences; -import android.graphics.drawable.Drawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; -import com.bumptech.glide.request.target.CustomTarget; -import com.bumptech.glide.request.transition.Transition; -import com.github.penfeizhou.animation.apng.APNGDrawable; -import com.github.penfeizhou.animation.apng.decode.APNGParser; -import com.github.penfeizhou.animation.gif.GifDrawable; -import com.github.penfeizhou.animation.gif.decode.GifParser; -import java.io.File; import java.util.List; import app.fedilab.android.R; @@ -76,28 +66,8 @@ public class EmojiAdapter extends BaseAdapter { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(holder.view.getContext()); boolean disableAnimatedEmoji = sharedpreferences.getBoolean(parent.getContext().getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); Glide.with(holder.binding.imgCustomEmoji.getContext()) - .asFile() .load(!disableAnimatedEmoji ? emoji.url : emoji.static_url) - .into(new CustomTarget() { - @Override - public void onResourceReady(@NonNull File resource, @Nullable Transition transition) { - if (APNGParser.isAPNG(resource.getAbsolutePath())) { - APNGDrawable apngDrawable = APNGDrawable.fromFile(resource.getAbsolutePath()); - holder.binding.imgCustomEmoji.setImageDrawable(apngDrawable); - } else if (GifParser.isGif(resource.getAbsolutePath())) { - GifDrawable gifDrawable = GifDrawable.fromFile(resource.getAbsolutePath()); - holder.binding.imgCustomEmoji.setImageDrawable(gifDrawable); - } else { - Drawable drawable = Drawable.createFromPath(resource.getAbsolutePath()); - holder.binding.imgCustomEmoji.setImageDrawable(drawable); - } - } - - @Override - public void onLoadCleared(@Nullable Drawable placeholder) { - - } - }); + .into(holder.binding.imgCustomEmoji); return holder.view; } From 1437976f3803d0365cffb133e38ff945c6fc800c Mon Sep 17 00:00:00 2001 From: Thomas Date: Sun, 10 Jul 2022 18:36:25 +0200 Subject: [PATCH 09/50] Fix issue #236 - Wrong custom emoji displayed when filtering --- .../fedilab/android/ui/drawer/ComposeAdapter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 4851b42a5..9a4f9d150 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 @@ -664,6 +664,7 @@ public class ComposeAdapter extends RecyclerView.Adapter emojisList = new ArrayList<>(); /** * Initialize text watcher for content writing * It will allow to complete autocomplete edit text while starting words with @, #, : etc. @@ -672,7 +673,6 @@ public class ComposeAdapter extends RecyclerView.Adapter[] emojis = new List[]{null}; String pattern = "(.|\\s)*(@[\\w_-]+@[a-z0-9.\\-]+|@[\\w_-]+)"; final Pattern mentionPattern = Pattern.compile(pattern); @@ -958,13 +958,13 @@ public class ComposeAdapter extends RecyclerView.Adapter { List emojisToDisplay = new ArrayList<>(); try { - if (emojis[0] == null) { - emojis[0] = new EmojiInstance(context).getEmojiList(BaseMainActivity.currentInstance); + if (emojisList == null || emojisList.size() == 0) { + emojisList = new EmojiInstance(context).getEmojiList(BaseMainActivity.currentInstance); } - if (emojis[0] == null) { + if (emojis == null) { return; } - for (Emoji emoji : emojis[0]) { + for (Emoji emoji : emojisList) { if (shortcode != null && emoji.shortcode.contains(shortcode)) { emojisToDisplay.add(emoji); if (emojisToDisplay.size() >= 10) { @@ -983,7 +983,7 @@ public class ComposeAdapter extends RecyclerView.Adapter 0) { final String search = searchA[searchA.length - 1]; holder.binding.content.setOnItemClickListener((parent, view, position, id) -> { - String shortcodeSelected = emojis[0].get(position).shortcode; + String shortcodeSelected = emojisToDisplay.get(position).shortcode; String deltaSearch = ""; int searchLength = searchDeep; if (currentCursorPosition < searchDeep) { //Less than 15 characters are written before the cursor position From 13d8876626404f349d2d2a40f32f67b3edeec39a Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 11 Jul 2022 07:34:33 +0200 Subject: [PATCH 10/50] Fix issue #241 - Store instance info in sharedpref --- .../app/fedilab/android/BaseMainActivity.java | 7 ++++++- .../android/activities/ComposeActivity.java | 11 ++++++++++ .../android/client/entities/api/Instance.java | 20 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 +- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index 343bbcc5d..19532dd72 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -786,7 +786,12 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt new ViewModelProvider(BaseMainActivity.this).get(InstancesVM.class).getEmoji(currentInstance); //Retrieve instance info new ViewModelProvider(BaseMainActivity.this).get(InstancesVM.class).getInstance(currentInstance) - .observe(BaseMainActivity.this, instance -> instanceInfo = instance.info); + .observe(BaseMainActivity.this, instance -> { + instanceInfo = instance.info; + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(getString(R.string.INSTANCE_INFO) + MainActivity.currentInstance, Instance.serialize(instanceInfo)); + editor.apply(); + }); //Retrieve filters new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getFilters(currentInstance, currentToken) .observe(BaseMainActivity.this, filters -> mainFilters = filters); diff --git a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java index 58c28c852..0a88e8d44 100644 --- a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java @@ -26,6 +26,7 @@ import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.drawable.ColorDrawable; import android.net.Uri; @@ -48,6 +49,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.lifecycle.ViewModelProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.work.Data; import androidx.work.OneTimeWorkRequest; @@ -69,6 +71,7 @@ import app.fedilab.android.R; import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.client.entities.api.Context; import app.fedilab.android.client.entities.api.EmojiInstance; +import app.fedilab.android.client.entities.api.Instance; import app.fedilab.android.client.entities.api.Mention; import app.fedilab.android.client.entities.api.ScheduledStatus; import app.fedilab.android.client.entities.api.Status; @@ -234,6 +237,14 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } }).start(); } + final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this); + if (MainActivity.instanceInfo == null) { + String instanceInfo = sharedpreferences.getString(getString(R.string.INSTANCE_INFO) + instance, null); + if (instanceInfo != null) { + MainActivity.instanceInfo = Instance.restore(instanceInfo); + } + } + StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class); //Empty compose List statusDraftList = new ArrayList<>(); diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Instance.java b/app/src/main/java/app/fedilab/android/client/entities/api/Instance.java index 3b9547efd..1ea2c7262 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Instance.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Instance.java @@ -1,5 +1,6 @@ package app.fedilab.android.client.entities.api; +import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; import java.io.Serializable; @@ -113,6 +114,25 @@ public class Instance implements Serializable { return mimeTypes; } + + public static String serialize(Instance instance) { + Gson gson = new Gson(); + try { + return gson.toJson(instance); + } catch (Exception e) { + return null; + } + } + + public static Instance restore(String serialized) { + Gson gson = new Gson(); + try { + return gson.fromJson(serialized, Instance.class); + } catch (Exception e) { + return null; + } + } + public static class Configuration implements Serializable { @SerializedName("statuses") public StatusesConf statusesConf; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c50d34396..726ab01d3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -895,7 +895,7 @@ SET_ACCOUNTS_PER_CALL SET_STATUSES_PER_CALL SET_NOTIFICATIONS_PER_CALL - + INSTANCE_INFO SET_INVIDIOUS SET_INVIDIOUS_HOST invidious.snopyta.org From 28ef0860aca8d10736c07c4ff8c068ed89c0e035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuz=20Ersen?= Date: Sun, 10 Jul 2022 18:58:51 +0000 Subject: [PATCH 11/50] Translated using Weblate (Turkish) Currently translated at 100.0% (726 of 726 strings) Translation: Fedilab/Strings Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/ --- app/src/main/res/values-tr/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 141ea7d3e..37cb1b2d8 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -726,4 +726,5 @@ Önbelleği temizle Diğer zaman çizelgeleri için önbellekteki mesajlar Taslaklarda saklanan mesajlar + Öntanımlı sistem dilini kullan \ No newline at end of file From 94c3dbcf25e7194624820dcdec241748c1ddb10d Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 11 Jul 2022 09:13:16 +0200 Subject: [PATCH 12/50] Add edit profile in main settings --- .../app/fedilab/android/BaseMainActivity.java | 5 - .../android/activities/SettingsActivity.java | 117 ++++++++++-------- app/src/main/res/layout/activity_settings.xml | 15 +++ 3 files changed, 78 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index 19532dd72..2c9218a02 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -1064,11 +1064,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt } - @Override - protected void onResume() { - super.onResume(); - } - public void redrawPinned(List mastodonLists) { int currentItem = binding.viewPager.getCurrentItem(); new ViewModelProvider(BaseMainActivity.this).get(TopBarVM.class).getDBPinned() diff --git a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java index 6bd43a2b3..44f24efea 100644 --- a/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/SettingsActivity.java @@ -16,6 +16,7 @@ package app.fedilab.android.activities; import static app.fedilab.android.BaseMainActivity.currentAccount; +import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.view.MenuItem; @@ -62,6 +63,7 @@ public class SettingsActivity extends BaseActivity { } canGoBack = false; + binding.setAccount.setOnClickListener(v -> displaySettings(SettingsEnum.ACCOUNT)); binding.setTimelines.setOnClickListener(v -> displaySettings(SettingsEnum.TIMELINES)); binding.setNotifications.setOnClickListener(v -> displaySettings(SettingsEnum.NOTIFICATIONS)); binding.setInterface.setOnClickListener(v -> displaySettings(SettingsEnum.INTERFACE)); @@ -79,61 +81,66 @@ public class SettingsActivity extends BaseActivity { public void displaySettings(SettingsEnum settingsEnum) { - ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { - FragmentManager fragmentManager = getSupportFragmentManager(); - FragmentTransaction fragmentTransaction = - fragmentManager.beginTransaction(); - String category = ""; - switch (settingsEnum) { - case TIMELINES: - FragmentTimelinesSettings fragmentTimelinesSettings = new FragmentTimelinesSettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentTimelinesSettings); - currentFragment = fragmentTimelinesSettings; - category = getString(R.string.settings_category_label_timelines); - break; - case NOTIFICATIONS: - FragmentNotificationsSettings fragmentNotificationsSettings = new FragmentNotificationsSettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentNotificationsSettings); - currentFragment = fragmentNotificationsSettings; - category = getString(R.string.notifications); - break; - case INTERFACE: - FragmentInterfaceSettings fragmentInterfaceSettings = new FragmentInterfaceSettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentInterfaceSettings); - currentFragment = fragmentInterfaceSettings; - category = getString(R.string.settings_category_label_interface); - break; - case COMPOSE: - FragmentComposeSettings fragmentComposeSettings = new FragmentComposeSettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentComposeSettings); - currentFragment = fragmentComposeSettings; - category = getString(R.string.compose); - break; - case PRIVACY: - FragmentPrivacySettings fragmentPrivacySettings = new FragmentPrivacySettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentPrivacySettings); - currentFragment = fragmentPrivacySettings; - category = getString(R.string.action_privacy); - break; - case THEMING: - FragmentThemingSettings fragmentThemingSettings = new FragmentThemingSettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentThemingSettings); - currentFragment = fragmentThemingSettings; - category = getString(R.string.theming); - break; - case LANGUAGE: - FragmentLanguageSettings fragmentLanguageSettings = new FragmentLanguageSettings(); - fragmentTransaction.replace(R.id.fragment_container, fragmentLanguageSettings); - currentFragment = fragmentLanguageSettings; - category = getString(R.string.languages); - break; + if (settingsEnum == SettingsEnum.ACCOUNT) { + Intent intent = new Intent(SettingsActivity.this, EditProfileActivity.class); + startActivity(intent); + } else { + ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = + fragmentManager.beginTransaction(); + String category = ""; + switch (settingsEnum) { + case TIMELINES: + FragmentTimelinesSettings fragmentTimelinesSettings = new FragmentTimelinesSettings(); + fragmentTransaction.replace(R.id.fragment_container, fragmentTimelinesSettings); + currentFragment = fragmentTimelinesSettings; + category = getString(R.string.settings_category_label_timelines); + break; + case NOTIFICATIONS: + FragmentNotificationsSettings fragmentNotificationsSettings = new FragmentNotificationsSettings(); + fragmentTransaction.replace(R.id.fragment_container, fragmentNotificationsSettings); + currentFragment = fragmentNotificationsSettings; + category = getString(R.string.notifications); + break; + case INTERFACE: + FragmentInterfaceSettings fragmentInterfaceSettings = new FragmentInterfaceSettings(); + fragmentTransaction.replace(R.id.fragment_container, fragmentInterfaceSettings); + currentFragment = fragmentInterfaceSettings; + category = getString(R.string.settings_category_label_interface); + break; + case COMPOSE: + FragmentComposeSettings fragmentComposeSettings = new FragmentComposeSettings(); + fragmentTransaction.replace(R.id.fragment_container, fragmentComposeSettings); + currentFragment = fragmentComposeSettings; + category = getString(R.string.compose); + break; + case PRIVACY: + FragmentPrivacySettings fragmentPrivacySettings = new FragmentPrivacySettings(); + fragmentTransaction.replace(R.id.fragment_container, fragmentPrivacySettings); + currentFragment = fragmentPrivacySettings; + category = getString(R.string.action_privacy); + break; + case THEMING: + FragmentThemingSettings fragmentThemingSettings = new FragmentThemingSettings(); + fragmentTransaction.replace(R.id.fragment_container, fragmentThemingSettings); + currentFragment = fragmentThemingSettings; + category = getString(R.string.theming); + break; + case LANGUAGE: + FragmentLanguageSettings fragmentLanguageSettings = new FragmentLanguageSettings(); + fragmentTransaction.replace(R.id.fragment_container, fragmentLanguageSettings); + currentFragment = fragmentLanguageSettings; + category = getString(R.string.languages); + break; - } - String title = String.format(Locale.getDefault(), "%s - %s", getString(R.string.settings), category); - setTitle(title); - canGoBack = true; - fragmentTransaction.commit(); - }); + } + String title = String.format(Locale.getDefault(), "%s - %s", getString(R.string.settings), category); + setTitle(title); + canGoBack = true; + fragmentTransaction.commit(); + }); + } } @@ -168,6 +175,8 @@ public class SettingsActivity extends BaseActivity { public enum SettingsEnum { + @SerializedName("ACCOUNT") + ACCOUNT("ACCOUNT"), @SerializedName("TIMELINES") TIMELINES("TIMELINES"), @SerializedName("NOTIFICATIONS") diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index d3169a5bf..117b1cf75 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -16,12 +16,27 @@ android:orientation="vertical" android:padding="24dp"> + + Date: Mon, 11 Jul 2022 13:34:01 +0200 Subject: [PATCH 13/50] Some changes --- .../client/entities/misskey/MisskeyNote.java | 7 +++-- .../android/helper/CrossActionHelper.java | 2 +- .../android/ui/drawer/StatusAdapter.java | 24 ++++++++--------- .../FragmentMastodonNotification.java | 19 -------------- .../timeline/FragmentMastodonTimeline.java | 6 ----- .../FragmentNotificationContainer.java | 26 +++++++++++++------ .../viewmodel/mastodon/TimelinesVM.java | 2 +- 7 files changed, 37 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java b/app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java index 438325317..0c5ccaf1f 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java +++ b/app/src/main/java/app/fedilab/android/client/entities/misskey/MisskeyNote.java @@ -55,7 +55,7 @@ public class MisskeyNote implements Serializable { @SerializedName("emojis") public List emojis; - public static Status convert(MisskeyNote misskeyNote) { + public static Status convert(MisskeyNote misskeyNote, String instance) { Status status = new Status(); status.id = misskeyNote.id; status.in_reply_to_id = misskeyNote.replyId; @@ -64,7 +64,10 @@ public class MisskeyNote implements Serializable { status.spoiler_text = misskeyNote.cw; status.visibility = misskeyNote.visibility; status.created_at = misskeyNote.createdAt; - status.uri = misskeyNote.uri; + if (misskeyNote.url == null) { + misskeyNote.url = "https://" + instance + "/notes/" + misskeyNote.id; + } + status.uri = misskeyNote.uri != null ? misskeyNote.uri : misskeyNote.url; status.url = misskeyNote.url; Account account = new Account(); diff --git a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java index 1b617e55d..48cea1a0b 100644 --- a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java @@ -149,7 +149,7 @@ public class CrossActionHelper { } }); } else if (targetedStatus != null) { - searchVM.search(ownerAccount.instance, ownerAccount.token, targetedStatus.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(ownerAccount.instance, ownerAccount.token, targetedStatus.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status status = results.statuses.get(0); 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 6761cfa7c..de1156c54 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 @@ -520,7 +520,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.actionButtonBookmark.setOnClickListener(v -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = statusList.get(0); @@ -545,7 +545,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.statusUserInfo.setOnClickListener(v -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -575,7 +575,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.statusBoosterAvatar.setOnClickListener(v -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -621,7 +621,7 @@ public class StatusAdapter extends RecyclerView.Adapter alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -649,7 +649,7 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -691,7 +691,7 @@ public class StatusAdapter extends RecyclerView.Adapter alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -719,7 +719,7 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -1094,7 +1094,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.reblogInfo.setOnClickListener(v -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -1121,7 +1121,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.favouriteInfo.setOnClickListener(v -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -1254,7 +1254,7 @@ public class StatusAdapter extends RecyclerView.Adapter //Vote on the poll if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -1336,7 +1336,7 @@ public class StatusAdapter extends RecyclerView.Adapter } else { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = results.statuses.get(0); @@ -1627,7 +1627,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.actionButtonReply.setOnClickListener(v -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); - searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.url, null, "statuses", false, true, false, 0, null, null, 1) + searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) .observe((LifecycleOwner) context, results -> { if (results.statuses != null && results.statuses.size() > 0) { Status fetchedStatus = statusList.get(0); 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 e17d319aa..6d3ce11f6 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 @@ -14,17 +14,14 @@ package app.fedilab.android.ui.fragment.timeline; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ -import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.service.notification.StatusBarNotification; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -178,22 +175,6 @@ public class FragmentMastodonNotification extends Fragment implements Notificati } - @Override - public void onResume() { - super.onResume(); - NotificationManager mNotificationManager = (NotificationManager) requireActivity().getSystemService(Context.NOTIFICATION_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && BaseMainActivity.currentAccount != null && BaseMainActivity.currentAccount.mastodon_account != null) { - for (StatusBarNotification statusBarNotification : mNotificationManager.getActiveNotifications()) { - if ((BaseMainActivity.currentAccount.mastodon_account.acct + "@" + BaseMainActivity.currentAccount.instance).equals(statusBarNotification.getGroupKey())) { - mNotificationManager.cancel(statusBarNotification.getId()); - } - } - } else { - mNotificationManager.cancelAll(); - } - } - - /** * Intialize the view for notifications * diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index a69d7d38d..b499fb7a8 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -30,7 +30,6 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -188,11 +187,6 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. } } - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - } - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentNotificationContainer.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentNotificationContainer.java index 5cc13408d..2e138d611 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentNotificationContainer.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentNotificationContainer.java @@ -14,15 +14,17 @@ package app.fedilab.android.ui.fragment.timeline; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ -import android.annotation.SuppressLint; +import android.app.NotificationManager; +import android.content.Context; import android.content.SharedPreferences; +import android.os.Build; import android.os.Bundle; +import android.service.notification.StatusBarNotification; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; @@ -238,6 +240,20 @@ public class FragmentNotificationContainer extends Fragment { return binding.getRoot(); } + @Override + public void onResume() { + super.onResume(); + NotificationManager mNotificationManager = (NotificationManager) requireActivity().getSystemService(Context.NOTIFICATION_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && BaseMainActivity.currentAccount != null && BaseMainActivity.currentAccount.mastodon_account != null) { + for (StatusBarNotification statusBarNotification : mNotificationManager.getActiveNotifications()) { + if (statusBarNotification.getGroupKey().contains(BaseMainActivity.currentAccount.mastodon_account.acct + "@" + BaseMainActivity.currentAccount.instance)) { + mNotificationManager.cancel(statusBarNotification.getId()); + } + } + } else { + mNotificationManager.cancelAll(); + } + } public void scrollToTop() { if (binding != null) { @@ -251,11 +267,5 @@ public class FragmentNotificationContainer extends Fragment { } } - @SuppressLint("ApplySharedPref") - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - } } 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 465ec9e59..019db2a11 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 @@ -274,7 +274,7 @@ public class TimelinesVM extends AndroidViewModel { List statusList = new ArrayList<>(); if (misskeyNoteList != null) { for (MisskeyNote misskeyNote : misskeyNoteList) { - Status status = MisskeyNote.convert(misskeyNote); + Status status = MisskeyNote.convert(misskeyNote, instance); statusList.add(status); } } From 0bdee201e78fdd87c708a2ca8a1584426f137e58 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 11 Jul 2022 16:40:50 +0200 Subject: [PATCH 14/50] Some tries --- app/build.gradle | 3 +- .../fedilab/android/helper/CustomEmoji.java | 130 ++++++++++ .../android/helper/SpannableHelper.java | 234 +----------------- .../android/ui/drawer/StatusAdapter.java | 7 + .../main/res/drawable/empty_custom_emoji.xml | 2 + 5 files changed, 144 insertions(+), 232 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/helper/CustomEmoji.java create mode 100644 app/src/main/res/drawable/empty_custom_emoji.xml diff --git a/app/build.gradle b/app/build.gradle index dc9ea8795..255d6f7c9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -99,8 +99,7 @@ dependencies { implementation project(path: ':cropper') annotationProcessor "com.github.bumptech.glide:compiler:4.12.0" implementation 'jp.wasabeef:glide-transformations:4.3.0' - implementation 'com.github.penfeizhou.android.animation:apng:2.22.0' - implementation 'com.github.penfeizhou.android.animation:gif:2.22.0' + implementation 'com.github.penfeizhou.android.animation:glide-plugin:2.22.0' implementation 'com.google.android.exoplayer:exoplayer:2.16.1' implementation "androidx.viewpager2:viewpager2:1.0.0" implementation 'com.github.piasy:rxandroidaudio:1.7.0' diff --git a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java new file mode 100644 index 000000000..02940cda6 --- /dev/null +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -0,0 +1,130 @@ +package app.fedilab.android.helper; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.text.Spannable; +import android.text.style.ReplacementSpan; +import android.util.Log; +import android.view.View; + +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; + +public class CustomEmoji extends ReplacementSpan { + + + private final View view; + private final float scale; + private Drawable imageDrawable; + + + CustomEmoji(WeakReference viewWeakReference) { + Context mContext = viewWeakReference.get().getContext(); + view = viewWeakReference.get(); + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); + scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.0f); + } + + public static void displayEmoji(List emojis, Spannable spannableString, WeakReference viewWeakReference) { + View view = viewWeakReference.get(); + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(view.getContext()); + boolean animate = !sharedpreferences.getBoolean(view.getContext().getString(R.string.SET_DISABLE_GIF), false); + for (Emoji emoji : emojis) { + Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) + .matcher(spannableString); + while (matcher.find()) { + CustomEmoji customEmoji = new CustomEmoji(viewWeakReference); + spannableString.setSpan(customEmoji, matcher.start(), matcher.end(), 0); + Glide.with(view) + .asDrawable() + .load(animate ? emoji.url : emoji.static_url) + .into(customEmoji.getTarget(animate)); + } + } + } + + @Override + public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { + Log.v(Helper.TAG, "fontMetricsInt: " + fontMetricsInt); + if (fontMetricsInt != null) { + Paint.FontMetrics fontMetrics = paint.getFontMetrics(); + fontMetricsInt.top = (int) fontMetrics.top; + fontMetricsInt.ascent = (int) fontMetrics.ascent; + fontMetricsInt.descent = (int) fontMetrics.descent; + fontMetricsInt.bottom = (int) fontMetrics.bottom; + } + return (int) (paint.getTextSize() * scale); + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + + if (imageDrawable != null) { + canvas.save(); + int emojiSize = (int) (paint.getTextSize() * scale); + Drawable drawable = imageDrawable; + drawable.setBounds(0, 0, emojiSize, emojiSize); + int transY = bottom - drawable.getBounds().bottom; + Log.v(Helper.TAG, "transY: " + transY); + transY -= paint.getFontMetrics().descent / 2; + Log.v(Helper.TAG, "transY: " + transY); + canvas.translate(x, (float) transY); + Log.v(Helper.TAG, "x: " + x); + drawable.draw(canvas); + canvas.restore(); + } + } + + public Target getTarget(boolean animate) { + return new CustomTarget() { + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + if (animate) { + Drawable.Callback callback = resource.getCallback(); + resource.setCallback(new Drawable.Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable drawable) { + callback.invalidateDrawable(drawable); + view.invalidate(); + } + + @Override + public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { + callback.scheduleDrawable(drawable, runnable, l); + } + + @Override + public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { + callback.unscheduleDrawable(drawable, runnable); + } + }); + } + Log.v(Helper.TAG, "imageDrawable2: " + imageDrawable); + imageDrawable = resource; + view.invalidate(); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + }; + } +} 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 b9d1df8cf..aaf6264a4 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -17,7 +17,6 @@ package app.fedilab.android.helper; import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.helper.Helper.USER_AGENT; -import static app.fedilab.android.helper.Helper.convertDpToPixel; import static app.fedilab.android.helper.Helper.urlPattern; import static app.fedilab.android.helper.ThemeHelper.linkColor; @@ -25,8 +24,6 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -38,7 +35,6 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.style.ClickableSpan; -import android.text.style.ImageSpan; import android.text.style.URLSpan; import android.util.Patterns; import android.view.LayoutInflater; @@ -47,16 +43,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; -import androidx.preference.PreferenceManager; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.FutureTarget; -import com.github.penfeizhou.animation.apng.APNGDrawable; -import com.github.penfeizhou.animation.apng.decode.APNGParser; -import com.github.penfeizhou.animation.gif.GifDrawable; -import com.github.penfeizhou.animation.gif.decode.GifParser; - -import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; @@ -65,7 +52,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -78,7 +64,6 @@ import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Attachment; -import app.fedilab.android.client.entities.api.Emoji; import app.fedilab.android.client.entities.api.Field; import app.fedilab.android.client.entities.api.Mention; import app.fedilab.android.client.entities.api.Poll; @@ -95,7 +80,7 @@ public class SpannableHelper { return convert(context, status, text, true); } /** - * Convert HTML content to text. Also, it handles click on link and transform emoji + * Convert HTML content to text. Also, it handles click on link * This needs to be run asynchronously * * @param context {@link Context} @@ -138,77 +123,6 @@ public class SpannableHelper { content.removeSpan(span); } - //--- EMOJI ---- - SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false); - List emojiList = status.reblog != null ? status.reblog.emojis : status.emojis; - //Will convert emoji if asked - if (emojiList != null && emojiList.size() > 0) { - for (Emoji emoji : emojiList) { - if (Helper.isValidContextForGlide(context)) { - FutureTarget futureTarget = Glide.with(context) - .asFile() - .load(disableGif ? emoji.static_url : emoji.url) - .submit(); - try { - File file = futureTarget.get(); - final String targetedEmoji = ":" + emoji.shortcode + ":"; - if (content.toString().contains(targetedEmoji)) { - //emojis can be used several times so we have to loop - for (int startPosition = -1; (startPosition = content.toString().indexOf(targetedEmoji, startPosition + 1)) != -1; startPosition++) { - final int endPosition = startPosition + targetedEmoji.length(); - if (endPosition <= content.toString().length() && endPosition >= startPosition) { - ImageSpan imageSpan; - if (APNGParser.isAPNG(file.getAbsolutePath())) { - APNGDrawable apngDrawable = APNGDrawable.fromFile(file.getAbsolutePath()); - try { - apngDrawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - apngDrawable.setVisible(true, true); - imageSpan = new ImageSpan(apngDrawable); - if (endPosition <= content.length()) { - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } catch (Exception ignored) { - } - } else if (GifParser.isGif(file.getAbsolutePath())) { - GifDrawable gifDrawable = GifDrawable.fromFile(file.getAbsolutePath()); - try { - gifDrawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - gifDrawable.setVisible(true, true); - imageSpan = new ImageSpan(gifDrawable); - if (endPosition <= content.length()) { - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } catch (Exception ignored) { - } - } else { - Drawable drawable = Drawable.createFromPath(file.getAbsolutePath()); - try { - drawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - drawable.setVisible(true, true); - imageSpan = new ImageSpan(drawable); - if (endPosition <= content.length()) { - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } catch (Exception ignored) { - } - } - } - } - } - } catch (ExecutionException | InterruptedException e) { - e.printStackTrace(); - } - } - } - } - //--- URLs ---- Matcher matcherLink = Patterns.WEB_URL.matcher(content); int offSetTruncate = 0; @@ -565,7 +479,7 @@ public class SpannableHelper { /** - * Convert HTML content to text. Also, it handles click on link and transform emoji + * Convert HTML content to text. Also, it handles click on link * This needs to be run asynchronously * * @param context {@link Context} @@ -603,77 +517,6 @@ public class SpannableHelper { content.removeSpan(span); } - //--- EMOJI ---- - SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false); - List emojiList = announcement.emojis; - //Will convert emoji if asked - if (emojiList != null && emojiList.size() > 0) { - for (Emoji emoji : emojiList) { - if (Helper.isValidContextForGlide(context)) { - FutureTarget futureTarget = Glide.with(context) - .asFile() - .load(disableGif ? emoji.static_url : emoji.url) - .submit(); - try { - File file = futureTarget.get(); - final String targetedEmoji = ":" + emoji.shortcode + ":"; - if (content.toString().contains(targetedEmoji)) { - //emojis can be used several times so we have to loop - for (int startPosition = -1; (startPosition = content.toString().indexOf(targetedEmoji, startPosition + 1)) != -1; startPosition++) { - final int endPosition = startPosition + targetedEmoji.length(); - if (endPosition <= content.toString().length() && endPosition >= startPosition) { - ImageSpan imageSpan; - if (APNGParser.isAPNG(file.getAbsolutePath())) { - APNGDrawable apngDrawable = APNGDrawable.fromFile(file.getAbsolutePath()); - try { - apngDrawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - apngDrawable.setVisible(true, true); - imageSpan = new ImageSpan(apngDrawable); - if (endPosition <= content.length()) { - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } catch (Exception ignored) { - } - } else if (GifParser.isGif(file.getAbsolutePath())) { - GifDrawable gifDrawable = GifDrawable.fromFile(file.getAbsolutePath()); - try { - gifDrawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - gifDrawable.setVisible(true, true); - imageSpan = new ImageSpan(gifDrawable); - if (endPosition <= content.length()) { - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } catch (Exception ignored) { - } - } else { - Drawable drawable = Drawable.createFromPath(file.getAbsolutePath()); - try { - drawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - drawable.setVisible(true, true); - imageSpan = new ImageSpan(drawable); - if (endPosition <= content.length()) { - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } catch (Exception ignored) { - } - } - } - } - } - } catch (ExecutionException | InterruptedException e) { - e.printStackTrace(); - } - } - } - } - //--- URLs ---- Matcher matcherLink = Patterns.WEB_URL.matcher(content); int offSetTruncate = 0; @@ -1035,7 +878,7 @@ public class SpannableHelper { } /** - * Convert HTML content to text. Also, it handles click on link and transform emoji + * Convert HTML content to text. Also, it handles click on link * This needs to be run asynchronously * * @param context {@link Context} @@ -1132,7 +975,7 @@ public class SpannableHelper { /** - * Convert HTML content to text. Also, it handles click on link and transform emoji + * Convert HTML content to text. Also, it handles click on link * This needs to be run asynchronously * * @param context {@link Context} @@ -1157,75 +1000,6 @@ public class SpannableHelper { URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); for (URLSpan span : urls) content.removeSpan(span); - //--- EMOJI ---- - SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false); - //Will convert emoji if asked - if (account.emojis != null && account.emojis.size() > 0) { - for (Emoji emoji : account.emojis) { - if (Helper.isValidContextForGlide(context)) { - FutureTarget futureTarget = Glide.with(context) - .asFile() - .load(disableGif ? emoji.static_url : emoji.url) - .submit(); - try { - File file = futureTarget.get(); - final String targetedEmoji = ":" + emoji.shortcode + ":"; - if (content.toString().contains(targetedEmoji)) { - //emojis can be used several times so we have to loop - for (int startPosition = -1; (startPosition = content.toString().indexOf(targetedEmoji, startPosition + 1)) != -1; startPosition++) { - final int endPosition = startPosition + targetedEmoji.length(); - if (endPosition <= content.toString().length() && endPosition >= startPosition) { - ImageSpan imageSpan; - if (APNGParser.isAPNG(file.getAbsolutePath())) { - APNGDrawable apngDrawable = APNGDrawable.fromFile(file.getAbsolutePath()); - try { - apngDrawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - apngDrawable.setVisible(true, true); - imageSpan = new ImageSpan(apngDrawable); - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - - } catch (Exception ignored) { - } - } else if (GifParser.isGif(file.getAbsolutePath())) { - GifDrawable gifDrawable = GifDrawable.fromFile(file.getAbsolutePath()); - try { - gifDrawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - gifDrawable.setVisible(true, true); - imageSpan = new ImageSpan(gifDrawable); - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } catch (Exception ignored) { - } - } else { - try { - Drawable drawable = Drawable.createFromPath(file.getAbsolutePath()); - drawable.setBounds(0, 0, (int) convertDpToPixel(20, context), (int) convertDpToPixel(20, context)); - drawable.setVisible(true, true); - imageSpan = new ImageSpan(drawable); - if (endPosition <= content.length()) { - content.setSpan( - imageSpan, startPosition, - endPosition, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - } catch (Exception ignored) { - } - } - } - } - } - } catch (ExecutionException | InterruptedException e) { - e.printStackTrace(); - } - } - } - } - if (limitedToDisplayName) { - return content; - } //--- URLs ---- Matcher matcherALink = Patterns.WEB_URL.matcher(content); 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 de1156c54..59369bc6e 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 @@ -82,6 +82,7 @@ import com.github.stom79.mytransl.translate.Params; import com.github.stom79.mytransl.translate.Translate; import com.varunest.sparkbutton.SparkButton; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -117,6 +118,7 @@ import app.fedilab.android.databinding.LayoutMediaBinding; import app.fedilab.android.databinding.LayoutPollItemBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.CrossActionHelper; +import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.GlideFocus; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.LongClickLinkMovementMethod; @@ -253,6 +255,7 @@ public class StatusAdapter extends RecyclerView.Adapter statusToDeal.bookmarked = statusReturned.bookmarked; statusToDeal.reblogs_count = statusReturned.reblogs_count; statusToDeal.favourites_count = statusReturned.favourites_count; + //Update status in cache if not a remote instance if (!remote) { new Thread(() -> { @@ -370,6 +373,10 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.actionButtonBookmark.setActiveImageTint(R.color.marked_icon); + if (statusToDeal.emojis != null && statusToDeal.emojis.size() > 0) { + CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, new WeakReference<>(holder.binding.statusContent)); + } + if (status.pinned) { holder.binding.statusPinned.setVisibility(View.VISIBLE); } else { diff --git a/app/src/main/res/drawable/empty_custom_emoji.xml b/app/src/main/res/drawable/empty_custom_emoji.xml new file mode 100644 index 000000000..7934ee66d --- /dev/null +++ b/app/src/main/res/drawable/empty_custom_emoji.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From 3b15c34e188c4858300be36384e62c1f4dc9c8d7 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 11 Jul 2022 16:54:51 +0200 Subject: [PATCH 15/50] Some tries --- .../app/fedilab/android/helper/CustomEmoji.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) 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 02940cda6..b80f0dc85 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -4,10 +4,10 @@ import android.content.Context; import android.content.SharedPreferences; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.style.ReplacementSpan; -import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -18,6 +18,7 @@ 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 com.github.penfeizhou.animation.apng.APNGDrawable; import java.lang.ref.WeakReference; import java.util.List; @@ -62,7 +63,6 @@ public class CustomEmoji extends ReplacementSpan { @Override public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { - Log.v(Helper.TAG, "fontMetricsInt: " + fontMetricsInt); if (fontMetricsInt != null) { Paint.FontMetrics fontMetrics = paint.getFontMetrics(); fontMetricsInt.top = (int) fontMetrics.top; @@ -75,18 +75,15 @@ public class CustomEmoji extends ReplacementSpan { @Override public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { - + if (imageDrawable != null) { canvas.save(); int emojiSize = (int) (paint.getTextSize() * scale); Drawable drawable = imageDrawable; drawable.setBounds(0, 0, emojiSize, emojiSize); int transY = bottom - drawable.getBounds().bottom; - Log.v(Helper.TAG, "transY: " + transY); transY -= paint.getFontMetrics().descent / 2; - Log.v(Helper.TAG, "transY: " + transY); canvas.translate(x, (float) transY); - Log.v(Helper.TAG, "x: " + x); drawable.draw(canvas); canvas.restore(); } @@ -96,7 +93,7 @@ public class CustomEmoji extends ReplacementSpan { return new CustomTarget() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { - if (animate) { + if (animate && resource instanceof Animatable) { Drawable.Callback callback = resource.getCallback(); resource.setCallback(new Drawable.Callback() { @Override @@ -115,10 +112,10 @@ public class CustomEmoji extends ReplacementSpan { callback.unscheduleDrawable(drawable, runnable); } }); + ((APNGDrawable) resource).start(); + imageDrawable = resource; + view.invalidate(); } - Log.v(Helper.TAG, "imageDrawable2: " + imageDrawable); - imageDrawable = resource; - view.invalidate(); } @Override From 270e722a38bbeda8c898f63cf7da0574d7a587e4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 11 Jul 2022 17:40:37 +0200 Subject: [PATCH 16/50] Some tries --- .../fedilab/android/helper/CustomEmoji.java | 31 +++++++++++++------ .../android/helper/MyAppGlideModule.java | 12 +++++++ .../android/ui/drawer/StatusAdapter.java | 28 ++--------------- 3 files changed, 36 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/app/fedilab/android/helper/MyAppGlideModule.java 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 b80f0dc85..ef7cdfa8b 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -8,6 +8,7 @@ import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.style.ReplacementSpan; +import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -18,7 +19,6 @@ 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 com.github.penfeizhou.animation.apng.APNGDrawable; import java.lang.ref.WeakReference; import java.util.List; @@ -34,30 +34,32 @@ public class CustomEmoji extends ReplacementSpan { private final View view; private final float scale; private Drawable imageDrawable; + private final WeakReference viewWeakReference; CustomEmoji(WeakReference viewWeakReference) { Context mContext = viewWeakReference.get().getContext(); + this.viewWeakReference = viewWeakReference; view = viewWeakReference.get(); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.0f); } - public static void displayEmoji(List emojis, Spannable spannableString, WeakReference viewWeakReference) { - View view = viewWeakReference.get(); + public static void displayEmoji(List emojis, Spannable spannableString, View view) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(view.getContext()); boolean animate = !sharedpreferences.getBoolean(view.getContext().getString(R.string.SET_DISABLE_GIF), false); for (Emoji emoji : emojis) { Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) .matcher(spannableString); while (matcher.find()) { - CustomEmoji customEmoji = new CustomEmoji(viewWeakReference); + CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); spannableString.setSpan(customEmoji, matcher.start(), matcher.end(), 0); Glide.with(view) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate)); } + } } @@ -93,29 +95,38 @@ public class CustomEmoji extends ReplacementSpan { return new CustomTarget() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + Log.v(Helper.TAG, "resource: " + resource); + Log.v(Helper.TAG, "instanceof: " + (resource instanceof Animatable)); + View view = viewWeakReference.get(); if (animate && resource instanceof Animatable) { Drawable.Callback callback = resource.getCallback(); resource.setCallback(new Drawable.Callback() { @Override public void invalidateDrawable(@NonNull Drawable drawable) { - callback.invalidateDrawable(drawable); + if (callback != null) { + callback.invalidateDrawable(drawable); + } view.invalidate(); } @Override public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { - callback.scheduleDrawable(drawable, runnable, l); + if (callback != null) { + callback.scheduleDrawable(drawable, runnable, l); + } } @Override public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { - callback.unscheduleDrawable(drawable, runnable); + if (callback != null) { + callback.unscheduleDrawable(drawable, runnable); + } } }); - ((APNGDrawable) resource).start(); - imageDrawable = resource; - view.invalidate(); + ((Animatable) resource).start(); } + imageDrawable = resource; + view.invalidate(); } @Override diff --git a/app/src/main/java/app/fedilab/android/helper/MyAppGlideModule.java b/app/src/main/java/app/fedilab/android/helper/MyAppGlideModule.java new file mode 100644 index 000000000..5b7b2b5fd --- /dev/null +++ b/app/src/main/java/app/fedilab/android/helper/MyAppGlideModule.java @@ -0,0 +1,12 @@ +package app.fedilab.android.helper; + +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.module.AppGlideModule; + +@GlideModule +public final class MyAppGlideModule extends AppGlideModule { + @Override + public boolean isManifestParsingEnabled() { + return false; + } +} \ No newline at end of file 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 59369bc6e..d837d330d 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 @@ -82,13 +82,10 @@ import com.github.stom79.mytransl.translate.Params; import com.github.stom79.mytransl.translate.Translate; import com.varunest.sparkbutton.SparkButton; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.Timer; -import java.util.TimerTask; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -372,9 +369,8 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.actionButtonBoost.setActiveImageTint(R.color.boost_icon); holder.binding.actionButtonBookmark.setActiveImageTint(R.color.marked_icon); - if (statusToDeal.emojis != null && statusToDeal.emojis.size() > 0) { - CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, new WeakReference<>(holder.binding.statusContent)); + CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent); } if (status.pinned) { @@ -930,6 +926,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.statusContent.setVisibility(View.GONE); holder.binding.mediaContainer.setVisibility(View.GONE); } + LayoutInflater inflater = ((Activity) context).getLayoutInflater(); //--- MEDIA ATTACHMENT --- if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) { @@ -1660,6 +1657,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.bindingReport.checkbox.setChecked(status.isChecked); holder.bindingReport.checkbox.setOnClickListener(v -> status.isChecked = !status.isChecked); } + } private static boolean mediaObfuscated(Status status) { @@ -1786,22 +1784,6 @@ public class StatusAdapter extends RecyclerView.Adapter 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, null, status, timelineType, minified, canBeFederated); - if (holder.timer != null) { - holder.timer.cancel(); - holder.timer = null; - } - if (status.emojis != null && status.emojis.size() > 0) { - holder.timer = new Timer(); - holder.timer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> holder.binding.statusContent.invalidate(); - mainHandler.post(myRunnable); - - } - }, 100, 100); - } } else if (viewHolder.getItemViewType() == STATUS_ART) { StatusViewHolder holder = (StatusViewHolder) viewHolder; MastodonHelper.loadPPMastodon(holder.bindingArt.artPp, status.account); @@ -1867,9 +1849,6 @@ public class StatusAdapter extends RecyclerView.Adapter @Override public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { super.onViewRecycled(holder); - if (holder instanceof StatusViewHolder && ((StatusViewHolder) holder).timer != null) { - ((StatusViewHolder) holder).timer.cancel(); - } } public interface FetchMoreCallBack { @@ -1885,7 +1864,6 @@ public class StatusAdapter extends RecyclerView.Adapter DrawerFetchMoreBinding bindingFetchMore; DrawerStatusNotificationBinding bindingNotification; DrawerStatusArtBinding bindingArt; - Timer timer; StatusViewHolder(DrawerStatusBinding itemView) { super(itemView.getRoot()); From aa3c6f07e7d75844b86dbefc827086e8a1731e9b Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 11 Jul 2022 18:15:55 +0200 Subject: [PATCH 17/50] Some tries --- .../app/fedilab/android/helper/CustomEmoji.java | 9 ++------- .../android/ui/drawer/StatusAdapter.java | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 11 deletions(-) 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 ef7cdfa8b..c45605d22 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -8,7 +8,6 @@ import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.style.ReplacementSpan; -import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -31,7 +30,6 @@ import app.fedilab.android.client.entities.api.Emoji; public class CustomEmoji extends ReplacementSpan { - private final View view; private final float scale; private Drawable imageDrawable; private final WeakReference viewWeakReference; @@ -40,21 +38,20 @@ public class CustomEmoji extends ReplacementSpan { CustomEmoji(WeakReference viewWeakReference) { Context mContext = viewWeakReference.get().getContext(); this.viewWeakReference = viewWeakReference; - view = viewWeakReference.get(); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.0f); } public static void displayEmoji(List emojis, Spannable spannableString, View view) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(view.getContext()); - boolean animate = !sharedpreferences.getBoolean(view.getContext().getString(R.string.SET_DISABLE_GIF), false); + boolean animate = !sharedpreferences.getBoolean(view.getContext().getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); for (Emoji emoji : emojis) { Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) .matcher(spannableString); while (matcher.find()) { CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); spannableString.setSpan(customEmoji, matcher.start(), matcher.end(), 0); - Glide.with(view) + Glide.with(view.getContext()) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into(customEmoji.getTarget(animate)); @@ -95,8 +92,6 @@ public class CustomEmoji extends ReplacementSpan { return new CustomTarget() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { - Log.v(Helper.TAG, "resource: " + resource); - Log.v(Helper.TAG, "instanceof: " + (resource instanceof Animatable)); View view = viewWeakReference.get(); if (animate && resource instanceof Animatable) { Drawable.Callback callback = resource.getCallback(); 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 d837d330d..84ade8340 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 @@ -369,9 +369,6 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.actionButtonBoost.setActiveImageTint(R.color.boost_icon); holder.binding.actionButtonBookmark.setActiveImageTint(R.color.marked_icon); - if (statusToDeal.emojis != null && statusToDeal.emojis.size() > 0) { - CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent); - } if (status.pinned) { holder.binding.statusPinned.setVisibility(View.VISIBLE); @@ -881,7 +878,19 @@ public class StatusAdapter extends RecyclerView.Adapter break; } //--- MAIN CONTENT --- - holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); + if (statusToDeal.emojis != null && statusToDeal.emojis.size() > 0) { + CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent); + holder.binding.statusContent.postDelayed(new Runnable() { + @Override + public void run() { + holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); + } + }, 100); + } else { + holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); + } + + if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); holder.binding.statusContent.setEllipsize(TextUtils.TruncateAt.END); From 346ac0610c6c3101947aec4411349f23c7aa5560 Mon Sep 17 00:00:00 2001 From: Ajeje Brazorf Date: Mon, 11 Jul 2022 21:42:41 +0000 Subject: [PATCH 18/50] Translated using Weblate (Sardinian) Currently translated at 100.0% (727 of 727 strings) Translation: Fedilab/Strings Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/ --- app/src/main/res/values-sc/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index a9016cd72..59ce3110c 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -720,4 +720,5 @@ Messàgios in memòria temporànea pro àteras lìnias de tempus Ses seguru de bòlere isboidare sa memòria temporànea\? Si tenes abbotzos cun cuntenutos multimediales as a pèrdere sos cuntenutos alligados. Isbòida sa memòria temporànea + Imprea sa limba predefinida de sistema \ No newline at end of file From d4e8df47a6b7f4ea83ca73548e4d88218ad9cbfa Mon Sep 17 00:00:00 2001 From: mastoduy Date: Mon, 11 Jul 2022 09:58:46 +0000 Subject: [PATCH 19/50] Translated using Weblate (Vietnamese) Currently translated at 100.0% (727 of 727 strings) Translation: Fedilab/Strings Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/vi/ --- app/src/main/res/values-vi/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 1a2b48cfc..b403a3caa 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -754,4 +754,5 @@ Bộ nhớ đệm tút những Bảng Tin khác Dung lượng cho phép Xóa bộ nhớ đệm + Dùng ngôn ngữ hệ thống \ No newline at end of file From 455e5666a9d8267b00105ea51ecac1768bee1dc3 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 13 Jul 2022 09:27:10 +0200 Subject: [PATCH 20/50] Some tries --- app/build.gradle | 6 +- .../android/client/entities/api/Account.java | 1 + .../android/client/entities/api/Status.java | 1 + .../fedilab/android/helper/CustomEmoji.java | 185 ++++++++---------- .../android/ui/drawer/StatusAdapter.java | 51 +++-- .../viewmodel/mastodon/StatusesVM.java | 4 +- 6 files changed, 133 insertions(+), 115 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 255d6f7c9..b41919ee1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -85,11 +85,9 @@ dependencies { implementation 'com.github.GrenderG:Toasty:1.5.2' implementation 'org.framagit.tom79:SparkButton:1.0.13' implementation "com.github.bumptech.glide:glide:4.12.0" + implementation "com.github.bumptech.glide:okhttp3-integration:4.12.0" + implementation 'com.github.mergehez:ArgPlayer:v3.1' - implementation ("com.github.bumptech.glide:recyclerview-integration:4.12.0") { - // Excludes the support library because it's already included by Glide. - transitive = false - } implementation project(path: ':mytransl') implementation project(path: ':ratethisapp') diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java index 1905ad42f..0f253477a 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java @@ -78,6 +78,7 @@ public class Account implements Serializable { public transient Spannable span_display_name; public transient Spannable span_note; public transient RelationShip relationShip; + public boolean emojiFetched = false; public static class AccountParams implements Serializable { @SerializedName("discoverable") 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 967364848..5224dca53 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 @@ -105,6 +105,7 @@ public class Status implements Serializable, Cloneable { public transient boolean setCursorToEnd = false; public transient int cursorPosition = 0; public transient boolean submitted = false; + public transient boolean emojiFetched = false; @NonNull public Object clone() throws CloneNotSupportedException { 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 c45605d22..801625f45 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -1,13 +1,24 @@ package app.fedilab.android.helper; +/* Copyright 2022 Thomas Schneider + * + * This file is a part of Fedilab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Fedilab; if not, + * see . */ import android.content.Context; import android.content.SharedPreferences; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.Spannable; -import android.text.style.ReplacementSpan; +import android.text.style.ImageSpan; import android.view.View; import androidx.annotation.NonNull; @@ -16,10 +27,9 @@ 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 com.github.penfeizhou.animation.apng.APNGDrawable; -import java.lang.ref.WeakReference; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,107 +37,86 @@ import java.util.regex.Pattern; import app.fedilab.android.R; import app.fedilab.android.client.entities.api.Emoji; -public class CustomEmoji extends ReplacementSpan { +public class CustomEmoji { - private final float scale; - private Drawable imageDrawable; - private final WeakReference viewWeakReference; - - - 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.0f); - } - - public static void displayEmoji(List emojis, Spannable spannableString, View view) { + public static void displayEmoji(Context context, List emojis, Spannable content, View view, String id, Callback listener) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(view.getContext()); boolean animate = !sharedpreferences.getBoolean(view.getContext().getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); + int count = 1; for (Emoji emoji : emojis) { - Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) - .matcher(spannableString); - while (matcher.find()) { - CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view)); - spannableString.setSpan(customEmoji, matcher.start(), matcher.end(), 0); - Glide.with(view.getContext()) - .asDrawable() - .load(animate ? emoji.url : emoji.static_url) - .into(customEmoji.getTarget(animate)); - } + int finalCount = count; + Glide.with(view.getContext()) + .asDrawable() + .load(animate ? emoji.url : emoji.static_url) + .into( + new CustomTarget() { + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (finalCount == emojis.size()) { + listener.allEmojisfound(id); + } + } + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) + .matcher(content); + while (matcher.find()) { + ImageSpan imageSpan; + resource.setBounds(0, 0, (int) Helper.convertDpToPixel(20, context), (int) Helper.convertDpToPixel(20, context)); + resource.setVisible(true, true); + imageSpan = new ImageSpan(resource); + content.setSpan( + imageSpan, matcher.start(), + matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + } + if (animate && resource instanceof APNGDrawable) { + Drawable.Callback callback = resource.getCallback(); + resource.setCallback(new Drawable.Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable drawable) { + if (callback != null) { + callback.invalidateDrawable(drawable); + } + view.invalidate(); + } + + @Override + public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { + if (callback != null) { + callback.scheduleDrawable(drawable, runnable, l); + } + } + + @Override + public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { + if (callback != null) { + callback.unscheduleDrawable(drawable, runnable); + } + } + }); + ((APNGDrawable) resource).start(); + + } + if (finalCount == emojis.size()) { + listener.allEmojisfound(id); + } + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + } + ); + count++; } } - @Override - public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { - if (fontMetricsInt != null) { - Paint.FontMetrics fontMetrics = paint.getFontMetrics(); - fontMetricsInt.top = (int) fontMetrics.top; - fontMetricsInt.ascent = (int) fontMetrics.ascent; - fontMetricsInt.descent = (int) fontMetrics.descent; - fontMetricsInt.bottom = (int) fontMetrics.bottom; - } - return (int) (paint.getTextSize() * scale); + public interface Callback { + void allEmojisfound(String id); } - @Override - public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { - - if (imageDrawable != null) { - canvas.save(); - int emojiSize = (int) (paint.getTextSize() * scale); - Drawable drawable = imageDrawable; - drawable.setBounds(0, 0, emojiSize, emojiSize); - int transY = bottom - drawable.getBounds().bottom; - transY -= paint.getFontMetrics().descent / 2; - canvas.translate(x, (float) transY); - drawable.draw(canvas); - canvas.restore(); - } - } - - public Target getTarget(boolean animate) { - return new CustomTarget() { - @Override - public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { - View view = viewWeakReference.get(); - if (animate && resource instanceof Animatable) { - Drawable.Callback callback = resource.getCallback(); - resource.setCallback(new Drawable.Callback() { - @Override - public void invalidateDrawable(@NonNull Drawable drawable) { - if (callback != null) { - callback.invalidateDrawable(drawable); - } - view.invalidate(); - } - - @Override - public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { - if (callback != null) { - callback.scheduleDrawable(drawable, runnable, l); - } - } - - @Override - public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { - if (callback != null) { - callback.unscheduleDrawable(drawable, runnable); - } - } - }); - ((Animatable) resource).start(); - } - imageDrawable = resource; - view.invalidate(); - } - - @Override - public void onLoadCleared(@Nullable Drawable placeholder) { - - } - }; - } } 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 84ade8340..bbd119d77 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 @@ -878,19 +878,18 @@ public class StatusAdapter extends RecyclerView.Adapter break; } //--- MAIN CONTENT --- - if (statusToDeal.emojis != null && statusToDeal.emojis.size() > 0) { - CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent); - holder.binding.statusContent.postDelayed(new Runnable() { - @Override - public void run() { - holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); + + CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent, status.id, id -> { + if (!status.emojiFetched) { + status.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; } - }, 100); - } else { - holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); - } - - + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); + // CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent, null, null); + holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); holder.binding.statusContent.setEllipsize(TextUtils.TruncateAt.END); @@ -1733,6 +1732,34 @@ public class StatusAdapter extends RecyclerView.Adapter return position; } + /** + * Will manage the current position of the element in the adapter. Action is async, and position might have changed + * + * @param notificationList List - Not null when calling from notification adapter + * @param statusList ist statusList - Not null when calling from status adapter + * @param id String - Current status + * @return int - position in real time + */ + public static int getPositionAsync(List notificationList, List statusList, String id) { + int position = 0; + if (statusList != null) { + for (Status _status : statusList) { + if (id != null && ((_status.id != null && _status.id.compareTo(id) == 0) || (_status.reblog != null && _status.reblog.id != null && _status.reblog.id.compareTo(id) == 0))) { + break; + } + position++; + } + } else if (notificationList != null) { + for (Notification notification : notificationList) { + if (notification.status != null && notification.status.id.compareTo(id) == 0) { + break; + } + position++; + } + } + return position; + } + @Override public int getItemViewType(int position) { if (timelineType == Timeline.TimeLineEnum.ART) { diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java index 39118e99c..c97c2daf0 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java @@ -461,7 +461,9 @@ public class StatusesVM extends AndroidViewModel { Handler mainHandler = new Handler(Looper.getMainLooper()); Accounts accountsPagination = new Accounts(); accountsPagination.accounts = accounts; - accountsPagination.pagination = MastodonHelper.getPagination(headers); + if (headers != null) { + accountsPagination.pagination = MastodonHelper.getPagination(headers); + } Runnable myRunnable = () -> accountsMutableLiveData.setValue(accountsPagination); mainHandler.post(myRunnable); }).start(); From c17322c699a50a917fe51f3d7546a4ca930b2950 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 13 Jul 2022 12:50:48 +0200 Subject: [PATCH 21/50] Some tries --- .../android/activities/ComposeActivity.java | 139 ++++++++---------- .../android/activities/ProfileActivity.java | 15 +- .../android/client/entities/api/Account.java | 2 +- .../client/entities/api/Announcement.java | 1 + .../fedilab/android/helper/CustomEmoji.java | 13 +- .../android/helper/SpannableHelper.java | 27 ++-- .../android/ui/drawer/AccountAdapter.java | 13 ++ .../ui/drawer/AnnouncementAdapter.java | 7 + .../android/ui/drawer/ComposeAdapter.java | 14 +- .../ui/drawer/ConversationAdapter.java | 13 ++ .../ui/drawer/NotificationAdapter.java | 26 +++- .../android/ui/drawer/StatusAdapter.java | 93 +++++++++++- .../viewmodel/mastodon/TimelinesVM.java | 2 +- 13 files changed, 257 insertions(+), 108 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java index 0a88e8d44..dc06ddcfc 100644 --- a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java @@ -268,86 +268,74 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } }); } else if (statusDraft != null) {//Restore a draft with all messages - new Thread(() -> { - if (statusDraft.statusReplyList != null) { - statusDraft.statusReplyList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusDraft.statusReplyList); - } - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - if (statusDraft.statusReplyList != null) { - statusList.addAll(statusDraft.statusReplyList); - binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList)); - } - int statusCount = statusList.size(); - statusList.addAll(statusDraft.statusDraftList); - composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility); - composeAdapter.manageDrafts = this; - LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); - binding.recyclerView.setLayoutManager(mLayoutManager); - binding.recyclerView.setAdapter(composeAdapter); - binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1); - }; - mainHandler.post(myRunnable); - }).start(); + if (statusDraft.statusReplyList != null) { + statusDraft.statusReplyList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusDraft.statusReplyList); + } + if (statusDraft.statusReplyList != null) { + statusList.addAll(statusDraft.statusReplyList); + binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList)); + } + int statusCount = statusList.size(); + statusList.addAll(statusDraft.statusDraftList); + composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility); + composeAdapter.manageDrafts = this; + LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); + binding.recyclerView.setLayoutManager(mLayoutManager); + binding.recyclerView.setAdapter(composeAdapter); + binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1); } else if (statusReply != null) { - new Thread(() -> { - statusReply = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusReply); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - statusList.add(statusReply); - int statusCount = statusList.size(); - statusDraftList.get(0).in_reply_to_id = statusReply.id; - //We change order for mentions - //At first place the account that has been mentioned if it's not our - statusDraftList.get(0).mentions = new ArrayList<>(); - if (!statusReply.account.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) { - Mention mention = new Mention(); - mention.acct = "@" + statusReply.account.acct; - mention.url = statusReply.account.url; - mention.username = statusReply.account.username; - statusDraftList.get(0).mentions.add(mention); - } + statusReply = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusReply); + statusList.add(statusReply); + int statusCount = statusList.size(); + statusDraftList.get(0).in_reply_to_id = statusReply.id; + //We change order for mentions + //At first place the account that has been mentioned if it's not our + statusDraftList.get(0).mentions = new ArrayList<>(); + if (!statusReply.account.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) { + Mention mention = new Mention(); + mention.acct = "@" + statusReply.account.acct; + mention.url = statusReply.account.url; + mention.username = statusReply.account.username; + statusDraftList.get(0).mentions.add(mention); + } - //There are other mentions to - if (statusReply.mentions != null && statusReply.mentions.size() > 0) { - for (Mention mentionTmp : statusReply.mentions) { - if (!mentionTmp.acct.equalsIgnoreCase(statusReply.account.acct) && !mentionTmp.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) { - statusDraftList.get(0).mentions.add(mentionTmp); - } - } + //There are other mentions to + if (statusReply.mentions != null && statusReply.mentions.size() > 0) { + for (Mention mentionTmp : statusReply.mentions) { + if (!mentionTmp.acct.equalsIgnoreCase(statusReply.account.acct) && !mentionTmp.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) { + statusDraftList.get(0).mentions.add(mentionTmp); } - if (mentionBooster != null) { - Mention mention = new Mention(); - mention.acct = mentionBooster.acct; - mention.url = mentionBooster.url; - mention.username = mentionBooster.username; - boolean present = false; - for (Mention mentionTmp : statusDraftList.get(0).mentions) { - if (mentionTmp.acct.equalsIgnoreCase(mentionBooster.acct)) { - present = true; - break; - } - } - if (!present) { - statusDraftList.get(0).mentions.add(mention); - } + } + } + if (mentionBooster != null) { + Mention mention = new Mention(); + mention.acct = mentionBooster.acct; + mention.url = mentionBooster.url; + mention.username = mentionBooster.username; + boolean present = false; + for (Mention mentionTmp : statusDraftList.get(0).mentions) { + if (mentionTmp.acct.equalsIgnoreCase(mentionBooster.acct)) { + present = true; + break; } - if (statusReply.spoiler_text != null) { - statusDraftList.get(0).spoiler_text = statusReply.spoiler_text; - } - //StatusDraftList at this point should only have one element - statusList.addAll(statusDraftList); - composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility); - composeAdapter.manageDrafts = this; - LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); - binding.recyclerView.setLayoutManager(mLayoutManager); - binding.recyclerView.setAdapter(composeAdapter); - statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id) - .observe(ComposeActivity.this, this::initializeContextView); - }; - mainHandler.post(myRunnable); - }).start(); + } + if (!present) { + statusDraftList.get(0).mentions.add(mention); + } + } + if (statusReply.spoiler_text != null) { + statusDraftList.get(0).spoiler_text = statusReply.spoiler_text; + } + //StatusDraftList at this point should only have one element + statusList.addAll(statusDraftList); + composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility); + composeAdapter.manageDrafts = this; + LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); + binding.recyclerView.setLayoutManager(mLayoutManager); + binding.recyclerView.setAdapter(composeAdapter); + statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id) + .observe(ComposeActivity.this, this::initializeContextView); } else { //Compose without replying statusList.addAll(statusDraftList); @@ -359,7 +347,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana if (statusMention != null) { composeAdapter.loadMentions(statusMention); } - } MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account); LocalBroadcastManager.getInstance(this) diff --git a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java index 1226c83d2..303b9f79e 100644 --- a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java @@ -32,6 +32,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.Html; +import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -89,6 +90,7 @@ import app.fedilab.android.client.entities.app.WellKnownNodeinfo; import app.fedilab.android.databinding.ActivityProfileBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.CrossActionHelper; +import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.SpannableHelper; @@ -362,7 +364,9 @@ public class ProfileActivity extends BaseActivity { if (account.span_display_name == null && account.display_name == null) { binding.accountDn.setText(account.username); } else { - binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE); + Spannable textAccount = account.span_display_name != null ? account.span_display_name : new SpannableString(account.display_name); + CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textAccount, binding.accountDn, null, null); + binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); } binding.accountUn.setText(String.format("@%s", account.acct)); @@ -377,12 +381,13 @@ public class ProfileActivity extends BaseActivity { clipboard.setPrimaryClip(clip); return false; }); - + Spannable textNote; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - binding.accountNote.setText(account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)), TextView.BufferType.SPANNABLE); + textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)); else - binding.accountNote.setText(account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)), TextView.BufferType.SPANNABLE); - + textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)); + CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textNote, binding.accountNote, null, null); + binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); MastodonHelper.loadPPMastodon(binding.accountPp, account); diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java index 0f253477a..012314ab7 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java @@ -78,7 +78,7 @@ public class Account implements Serializable { public transient Spannable span_display_name; public transient Spannable span_note; public transient RelationShip relationShip; - public boolean emojiFetched = false; + public transient boolean emojiFetched = false; public static class AccountParams implements Serializable { @SerializedName("discoverable") diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java index 4669833d7..287e245ee 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java @@ -51,4 +51,5 @@ public class Announcement { //Some extra spannable element - They will be filled automatically when fetching the status public transient Spannable span_content; + public boolean emojiFetched = false; } 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 801625f45..6f3745250 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -16,6 +16,7 @@ package app.fedilab.android.helper; import android.content.Context; import android.content.SharedPreferences; +import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.style.ImageSpan; @@ -28,7 +29,6 @@ import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; -import com.github.penfeizhou.animation.apng.APNGDrawable; import java.util.List; import java.util.regex.Matcher; @@ -54,13 +54,16 @@ public class CustomEmoji { @Override public void onLoadFailed(@Nullable Drawable errorDrawable) { super.onLoadFailed(errorDrawable); - if (finalCount == emojis.size()) { + if (finalCount == emojis.size() && listener != null) { listener.allEmojisfound(id); } } @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + if (content == null) { + return; + } Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) .matcher(content); while (matcher.find()) { @@ -72,7 +75,7 @@ public class CustomEmoji { imageSpan, matcher.start(), matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } - if (animate && resource instanceof APNGDrawable) { + if (animate && resource instanceof Animatable) { Drawable.Callback callback = resource.getCallback(); resource.setCallback(new Drawable.Callback() { @Override @@ -97,10 +100,10 @@ public class CustomEmoji { } } }); - ((APNGDrawable) resource).start(); + ((Animatable) resource).start(); } - if (finalCount == emojis.size()) { + if (finalCount == emojis.size() && listener != null) { listener.allEmojisfound(id); } } 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 aaf6264a4..3616f53ac 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -861,31 +861,29 @@ public class SpannableHelper { return statuses; } - public static List convertNitterStatus(Context context, List statuses) { + public static List convertNitterStatus(List statuses) { if (statuses != null) { for (Status status : statuses) { - convertNitterStatus(context, status); + convertNitterStatus(status); } } return statuses; } - public static Status convertNitterStatus(Context context, Status status) { + public static void convertNitterStatus(Status status) { if (status != null) { - status.span_content = SpannableHelper.convertNitter(context, status.content); + status.span_content = SpannableHelper.convertNitter(status.content); } - return status; } /** * Convert HTML content to text. Also, it handles click on link * This needs to be run asynchronously * - * @param context {@link Context} - * @param text String - text to convert, it can be content, spoiler, poll items, etc. + * @param text String - text to convert, it can be content, spoiler, poll items, etc. * @return Spannable string */ - private static Spannable convertNitter(@NonNull Context context, String text) { + private static Spannable convertNitter(String text) { SpannableString initialContent; if (text == null) { return null; @@ -924,7 +922,7 @@ public class SpannableHelper { if (status.account == null) { return status; } - status.account.span_display_name = SpannableHelper.convertA(context, status.account, status.account.display_name, true); + status.account.span_display_name = SpannableHelper.convertA(context, status.account.display_name, true); if (status.poll != null) { for (Poll.PollItem pollItem : status.poll.options) { pollItem.span_title = SpannableHelper.convert(context, status, pollItem.title, false); @@ -936,7 +934,7 @@ public class SpannableHelper { status.reblog.span_translate = SpannableHelper.convert(context, status, status.reblog.translationContent); } status.reblog.span_spoiler_text = SpannableHelper.convert(context, status, status.reblog.spoiler_text); - status.reblog.account.span_display_name = SpannableHelper.convertA(context, status.reblog.account, status.reblog.account.display_name, true); + status.reblog.account.span_display_name = SpannableHelper.convertA(context, status.reblog.account.display_name, true); if (status.reblog.poll != null) { for (Poll.PollItem pollItem : status.reblog.poll.options) { pollItem.span_title = SpannableHelper.convert(context, status, pollItem.title, false); @@ -959,12 +957,12 @@ public class SpannableHelper { public static Account convertAccount(Context context, Account account) { if (account != null) { - account.span_display_name = SpannableHelper.convertA(context, account, account.display_name, true); - account.span_note = SpannableHelper.convertA(context, account, account.note, false); + account.span_display_name = SpannableHelper.convertA(context, account.display_name, true); + account.span_note = SpannableHelper.convertA(context, account.note, false); if (account.fields != null && account.fields.size() > 0) { List fields = new ArrayList<>(); for (Field field : account.fields) { - field.value_span = SpannableHelper.convertA(context, account, field.value, false); + field.value_span = SpannableHelper.convertA(context, field.value, false); fields.add(field); } account.fields = fields; @@ -979,11 +977,10 @@ public class SpannableHelper { * This needs to be run asynchronously * * @param context {@link Context} - * @param account {@link Account} - Account concerned by the spannable transformation * @param text String - text to convert, it can be display name or bio * @return Spannable string */ - private static Spannable convertA(@NonNull Context context, @NonNull Account account, String text, boolean limitedToDisplayName) { + private static Spannable convertA(@NonNull Context context, String text, boolean limitedToDisplayName) { SpannableString initialContent; if (text == null) { return null; diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java index 6f6e053d3..05e1b2fa9 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java @@ -45,6 +45,7 @@ import app.fedilab.android.R; import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.databinding.DrawerAccountBinding; +import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.AccountsVM; @@ -224,8 +225,20 @@ public class AccountAdapter extends RecyclerView.Adapter { + if (!account.emojiFetched) { + account.emojiFetched = true; + accountViewHolder.binding.displayName.post(() -> adapter.notifyItemChanged(position)); + } + }); accountViewHolder.binding.displayName.setText(account.span_display_name, TextView.BufferType.SPANNABLE); accountViewHolder.binding.username.setText(String.format("@%s", account.acct)); + CustomEmoji.displayEmoji(context, account.emojis, account.span_note, accountViewHolder.binding.bio, account.id, id -> { + if (!account.emojiFetched) { + account.emojiFetched = true; + accountViewHolder.binding.bio.post(() -> adapter.notifyItemChanged(position)); + } + }); accountViewHolder.binding.bio.setText(account.span_note, TextView.BufferType.SPANNABLE); } diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java index 4983cbda0..8ae35dafd 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java @@ -44,6 +44,7 @@ import app.fedilab.android.R; import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Reaction; import app.fedilab.android.databinding.DrawerAnnouncementBinding; +import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.viewmodel.mastodon.AnnouncementsVM; @@ -87,6 +88,12 @@ public class AnnouncementAdapter extends RecyclerView.Adapter { + if (!announcement.emojiFetched) { + announcement.emojiFetched = true; + holder.binding.content.post(() -> notifyItemChanged(position)); + } + }); holder.binding.content.setText(announcement.span_content, TextView.BufferType.SPANNABLE); if (announcement.starts_at != null) { String dateIni; 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 9a4f9d150..2ce9f3fa5 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 @@ -103,6 +103,7 @@ import app.fedilab.android.databinding.DrawerStatusComposeBinding; import app.fedilab.android.databinding.DrawerStatusSimpleBinding; import app.fedilab.android.databinding.PopupMediaDescriptionBinding; import app.fedilab.android.exception.DBException; +import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.ThemeHelper; @@ -1035,12 +1036,23 @@ public class ComposeAdapter extends RecyclerView.Adapter { + if (!status.emojiFetched) { + status.emojiFetched = true; + holder.binding.statusContent.post(() -> notifyItemChanged(position)); + } + }); holder.binding.statusContent.setText(status.span_content, TextView.BufferType.SPANNABLE); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); + CustomEmoji.displayEmoji(context, status.account.emojis, status.account.span_display_name, holder.binding.displayName, status.id, id -> { + if (!status.account.emojiFetched) { + status.account.emojiFetched = true; + holder.binding.statusContent.post(() -> notifyItemChanged(position)); + } + }); holder.binding.displayName.setText(status.account.span_display_name, TextView.BufferType.SPANNABLE); holder.binding.username.setText(String.format("@%s", status.account.acct)); if (status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) { - holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setText(status.span_spoiler_text, TextView.BufferType.SPANNABLE); } else { 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 c074b16a3..70f2484d7 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 @@ -49,6 +49,7 @@ import app.fedilab.android.client.entities.api.Conversation; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.databinding.DrawerConversationBinding; import app.fedilab.android.databinding.ThumbnailBinding; +import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; @@ -137,6 +138,12 @@ public class ConversationAdapter extends RecyclerView.Adapter { + if (!conversation.last_status.emojiFetched) { + conversation.last_status.emojiFetched = true; + holder.binding.spoiler.post(() -> notifyItemChanged(position)); + } + }); holder.binding.spoiler.setText(conversation.last_status.span_spoiler_text, TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); @@ -144,6 +151,12 @@ public class ConversationAdapter extends RecyclerView.Adapter { + if (!conversation.last_status.emojiFetched) { + conversation.last_status.emojiFetched = true; + holder.binding.statusContent.post(() -> notifyItemChanged(position)); + } + }); holder.binding.statusContent.setText(conversation.last_status.span_content, 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 7a121691c..4cbae4f0c 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 @@ -24,6 +24,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.app.ActivityOptionsCompat; @@ -42,6 +43,7 @@ import app.fedilab.android.databinding.DrawerFetchMoreBinding; import app.fedilab.android.databinding.DrawerFollowBinding; import app.fedilab.android.databinding.DrawerStatusNotificationBinding; import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding; +import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.SearchVM; @@ -120,7 +122,13 @@ public class NotificationAdapter extends RecyclerView.Adapter { + if (!notification.account.emojiFetched) { + notification.account.emojiFetched = true; + holderFollow.binding.displayName.post(() -> notifyItemChanged(position)); + } + }); + holderFollow.binding.displayName.setText(notification.account.span_display_name, TextView.BufferType.SPANNABLE); holderFollow.binding.username.setText(String.format("@%s", notification.account.acct)); if (getItemViewType(position) == TYPE_FOLLOW_REQUEST) { holderFollow.binding.rejectButton.setVisibility(View.VISIBLE); @@ -188,7 +196,13 @@ public class NotificationAdapter extends RecyclerView.Adapter { + if (!notification.account.emojiFetched) { + notification.account.emojiFetched = true; + holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); + } + }); + holderStatus.bindingNotification.status.displayName.setText(title, TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.containerTransparent.setAlpha(.1f); if (notification.status != null && notification.status.visibility.equalsIgnoreCase("direct")) { @@ -255,7 +269,13 @@ public class NotificationAdapter extends RecyclerView.Adapter { + if (!notification.account.emojiFetched) { + notification.account.emojiFetched = true; + holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); + } + }); + holderStatus.bindingNotification.status.displayName.setText(title, TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.status.actionButtons.setVisibility(View.GONE); } 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 bbd119d77..631031e29 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 @@ -750,6 +750,16 @@ public class StatusAdapter extends RecyclerView.Adapter if (span_display_name == null || span_display_name.toString().trim().length() == 0) { span_display_name = new SpannableString(statusToDeal.account.username); } + + CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, span_display_name, holder.binding.displayName, status.id, id -> { + if (!statusToDeal.account.emojiFetched) { + statusToDeal.account.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); holder.binding.displayName.setText(span_display_name, TextView.BufferType.SPANNABLE); if (theme_text_header_1_line != -1) { holder.binding.displayName.setTextColor(theme_text_header_1_line); @@ -815,6 +825,15 @@ public class StatusAdapter extends RecyclerView.Adapter if (expand_cw || expand) { holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE); + CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_spoiler_text, holder.binding.spoiler, status.id, id -> { + if (!statusToDeal.emojiFetched) { + statusToDeal.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE); statusToDeal.isExpended = true; statusToDeal.isMediaDisplayed = true; @@ -826,6 +845,15 @@ public class StatusAdapter extends RecyclerView.Adapter }); holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE); + CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_spoiler_text, holder.binding.spoiler, status.id, id -> { + if (!statusToDeal.emojiFetched) { + statusToDeal.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE); } if (statusToDeal.isExpended) { @@ -846,6 +874,15 @@ public class StatusAdapter extends RecyclerView.Adapter if (span_display_name_boost == null || span_display_name_boost.toString().trim().length() == 0) { span_display_name_boost = new SpannableString(status.account.username); } + CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, span_display_name_boost, holder.binding.statusBoosterDisplayName, status.id, id -> { + if (!statusToDeal.account.emojiFetched) { + statusToDeal.account.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); holder.binding.statusBoosterDisplayName.setText(span_display_name_boost, TextView.BufferType.SPANNABLE); holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE); holder.binding.boosterDivider.setVisibility(View.VISIBLE); @@ -888,7 +925,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); } }); - // CustomEmoji.displayEmoji(statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent, null, null); + holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); @@ -920,6 +957,15 @@ public class StatusAdapter extends RecyclerView.Adapter } if (statusToDeal.translationContent != null) { holder.binding.containerTrans.setVisibility(View.VISIBLE); + CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_translate, holder.binding.statusContentTranslated, status.id, id -> { + if (!statusToDeal.emojiFetched) { + statusToDeal.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); holder.binding.statusContentTranslated.setText(statusToDeal.span_translate, TextView.BufferType.SPANNABLE); } else { holder.binding.containerTrans.setVisibility(View.GONE); @@ -1184,6 +1230,15 @@ public class StatusAdapter extends RecyclerView.Adapter pollItemBinding.pollItemPercent.setTextColor(theme_text_color); pollItemBinding.pollItemText.setTextColor(theme_text_color); } + CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollItem.span_title, pollItemBinding.pollItemText, status.id, id -> { + if (!statusToDeal.emojiFetched) { + statusToDeal.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); pollItemBinding.pollItemText.setText(pollItem.span_title, TextView.BufferType.SPANNABLE); pollItemBinding.pollItemValue.setProgress((int) value); if (pollItem.votes_count == greaterValue) { @@ -1212,6 +1267,15 @@ public class StatusAdapter extends RecyclerView.Adapter for (Poll.PollItem pollOption : statusToDeal.poll.options) { CheckBox cb = new CheckBox(context); cb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); + CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollOption.span_title, cb, status.id, id -> { + if (!statusToDeal.emojiFetched) { + statusToDeal.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); cb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); holder.binding.poll.multipleChoice.addView(cb); } @@ -1223,6 +1287,24 @@ public class StatusAdapter extends RecyclerView.Adapter for (Poll.PollItem pollOption : statusToDeal.poll.options) { RadioButton rb = new RadioButton(context); rb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); + CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, pollOption.span_title, rb, status.id, id -> { + if (!statusToDeal.account.emojiFetched) { + statusToDeal.account.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); + CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollOption.span_title, rb, status.id, id -> { + if (!statusToDeal.emojiFetched) { + statusToDeal.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); + } + }); rb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); holder.binding.poll.singleChoiceRadioGroup.addView(rb); } @@ -1827,6 +1909,15 @@ public class StatusAdapter extends RecyclerView.Adapter .load(status.art_attachment.preview_url) .apply(new RequestOptions().transform(new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) .into(holder.bindingArt.artMedia); + CustomEmoji.displayEmoji(context, status.emojis, status.account.span_display_name, holder.bindingArt.artAcct, status.id, id -> { + if (!status.emojiFetched) { + status.emojiFetched = true; + if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { + return; + } + notifyItemChanged(getPositionAsync(null, statusList, id)); + } + }); holder.bindingArt.artAcct.setText(status.account.span_display_name, TextView.BufferType.SPANNABLE); holder.bindingArt.artUsername.setText(String.format(Locale.getDefault(), "@%s", status.account.acct)); holder.bindingArt.artPp.setOnClickListener(v -> { 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 019db2a11..47defbdf5 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 @@ -232,7 +232,7 @@ public class TimelinesVM extends AndroidViewModel { statusList.add(status); } } - statuses.statuses = SpannableHelper.convertNitterStatus(getApplication().getApplicationContext(), statusList); + statuses.statuses = SpannableHelper.convertNitterStatus(statusList); String max_id = publicTlResponse.headers().get("min-id"); statuses.pagination = new Pagination(); statuses.pagination.max_id = max_id; From 2436c839dc5769b1798783001b259b198c50d459 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 13 Jul 2022 12:56:07 +0200 Subject: [PATCH 22/50] Some tries --- .../android/ui/drawer/NotificationAdapter.java | 12 +++++++----- .../app/fedilab/android/ui/drawer/StatusAdapter.java | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) 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 4cbae4f0c..542f66c2a 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 @@ -21,6 +21,8 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.text.Spannable; +import android.text.SpannableString; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -213,14 +215,14 @@ public class NotificationAdapter extends RecyclerView.Adapter 0) { if (notification.type.equals("favourite")) { @@ -269,7 +271,7 @@ public class NotificationAdapter extends RecyclerView.Adapter { + CustomEmoji.displayEmoji(context, notification.account.emojis, title, holderStatus.binding.displayName, notification.id, id -> { if (!notification.account.emojiFetched) { notification.account.emojiFetched = true; holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/StatusAdapter.java index 631031e29..2069205ff 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 @@ -572,7 +572,7 @@ public class StatusAdapter extends RecyclerView.Adapter context.startActivity(intent, options.toBundle()); } }); - holder.binding.statusBoosterAvatar.setOnClickListener(v -> { + holder.binding.statusBoosterInfo.setOnClickListener(v -> { if (remote) { Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.uri, null, "statuses", false, true, false, 0, null, null, 1) From d58a92f1675e497643e6ef464f1672747a878e19 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 13 Jul 2022 18:55:24 +0200 Subject: [PATCH 23/50] Remove remote fetch accounts --- .../android/activities/ProfileActivity.java | 8 +- .../android/helper/SpannableHelper.java | 234 ++++++++---------- .../ui/drawer/NotificationAdapter.java | 5 + 3 files changed, 108 insertions(+), 139 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java index 303b9f79e..5bfdabab6 100644 --- a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java @@ -150,8 +150,6 @@ public class ProfileActivity extends BaseActivity { account_id = b.getString(Helper.ARG_USER_ID, null); mention_str = b.getString(Helper.ARG_MENTION, null); } - - postponeEnterTransition(); //Remove title @@ -365,7 +363,9 @@ public class ProfileActivity extends BaseActivity { binding.accountDn.setText(account.username); } else { Spannable textAccount = account.span_display_name != null ? account.span_display_name : new SpannableString(account.display_name); - CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textAccount, binding.accountDn, null, null); + CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textAccount, binding.accountDn, null, id -> { + binding.accountDn.invalidate(); + }); binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); } @@ -386,7 +386,7 @@ public class ProfileActivity extends BaseActivity { textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)); else textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)); - CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textNote, binding.accountNote, null, null); + CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textNote, binding.accountNote, null, id -> binding.accountNote.invalidate()); binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); 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 3616f53ac..c164ae352 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -123,6 +123,102 @@ public class SpannableHelper { content.removeSpan(span); } + // --- For all patterns defined in Helper class --- + for (Map.Entry entry : Helper.patternHashMap.entrySet()) { + Helper.PatternType patternType = entry.getKey(); + Pattern pattern = entry.getValue(); + Matcher matcher = pattern.matcher(content); + while (matcher.find()) { + int matchStart = matcher.start(); + int matchEnd = matcher.end(); + String word = content.toString().substring(matchStart, matchEnd); + if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { + URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); + content.removeSpan(span); + content.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + textView.setTag(CLICKABLE_SPAN); + switch (patternType) { + case TAG: + Intent intent = new Intent(context, HashTagActivity.class); + Bundle b = new Bundle(); + b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + case GROUP: + break; + case MENTION: + intent = new Intent(context, ProfileActivity.class); + b = new Bundle(); + Mention targetedMention = null; + HashMap countUsername = new HashMap<>(); + + for (Mention mention : status.mentions) { + Integer count = countUsername.get(mention.username); + if (count == null) { + count = 0; + } + if (countUsername.containsKey(mention.username)) { + countUsername.put(mention.username, count + 1); + } else { + countUsername.put(mention.username, 1); + } + } + for (Mention mention : status.mentions) { + Integer count = countUsername.get(mention.username); + if (count == null) { + count = 0; + } + if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { + targetedMention = mention; + break; + } + } + if (targetedMention != null) { + b.putString(Helper.ARG_USER_ID, targetedMention.id); + } else { + b.putString(Helper.ARG_MENTION, word.trim()); + } + + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + case MENTION_LONG: + intent = new Intent(context, ProfileActivity.class); + b = new Bundle(); + targetedMention = null; + for (Mention mention : status.mentions) { + if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { + targetedMention = mention; + break; + } + } + if (targetedMention != null) { + b.putString(Helper.ARG_USER_ID, targetedMention.id); + } else { + b.putString(Helper.ARG_MENTION, word.trim()); + } + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(linkColor); + } + }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } //--- URLs ---- Matcher matcherLink = Patterns.WEB_URL.matcher(content); int offSetTruncate = 0; @@ -166,6 +262,7 @@ public class SpannableHelper { } } + if (matchEnd <= content.length() && matchEnd >= matchStart) { content.setSpan(new LongClickableSpan() { @Override @@ -300,46 +397,7 @@ public class SpannableHelper { @Override public void onClick(@NonNull View textView) { textView.setTag(CLICKABLE_SPAN); - Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$"); - Matcher matcherLink = link.matcher(url); - if (matcherLink.find() && !url.contains("medium.com")) { - if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot - CrossActionHelper.fetchRemoteStatus(context, currentAccount, url, new CrossActionHelper.Callback() { - @Override - public void federatedStatus(Status status) { - Intent intent = new Intent(context, ContextActivity.class); - intent.putExtra(Helper.ARG_STATUS, status); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - - @Override - public void federatedAccount(Account account) { - - } - }); - } else {//It's an account - CrossActionHelper.fetchRemoteAccount(context, currentAccount, status.account, new CrossActionHelper.Callback() { - @Override - public void federatedStatus(Status status) { - - } - - @Override - public void federatedAccount(Account account) { - Intent intent = new Intent(context, ProfileActivity.class); - Bundle b = new Bundle(); - b.putSerializable(Helper.ARG_ACCOUNT, account); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - }); - } - } else { - Helper.openBrowser(context, newURL); - } - + Helper.openBrowser(context, newURL); } @Override @@ -352,102 +410,7 @@ public class SpannableHelper { } } - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - String word = content.toString().substring(matchStart, matchEnd); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - Mention targetedMention = null; - HashMap countUsername = new HashMap<>(); - for (Mention mention : status.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (countUsername.containsKey(mention.username)) { - countUsername.put(mention.username, count + 1); - } else { - countUsername.put(mention.username, 1); - } - } - for (Mention mention : status.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { - targetedMention = mention; - break; - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case MENTION_LONG: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - targetedMention = null; - for (Mention mention : status.mentions) { - if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { - targetedMention = mention; - break; - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - } Matcher matcher = Helper.ouichesPattern.matcher(content); while (matcher.find()) { @@ -803,6 +766,7 @@ public class SpannableHelper { break; } } + if (targetedMention != null) { b.putString(Helper.ARG_USER_ID, targetedMention.id); } else { 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 542f66c2a..86bcf4a22 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 @@ -124,6 +124,11 @@ public class NotificationAdapter extends RecyclerView.Adapter { if (!notification.account.emojiFetched) { notification.account.emojiFetched = true; From 2db7b4b87c647ec22760471c854aa539a146b5ce Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 14 Jul 2022 11:04:27 +0200 Subject: [PATCH 24/50] Fix mentions --- .../app/fedilab/android/helper/Helper.java | 6 +-- .../android/helper/SpannableHelper.java | 44 ++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) 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 8029b84a7..c3111a67d 100644 --- a/app/src/main/java/app/fedilab/android/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/helper/Helper.java @@ -119,7 +119,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Date; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -352,9 +352,9 @@ public class Helper { public static int counter = 1; static { - Map aMap = new HashMap<>(); - aMap.put(PatternType.MENTION, mentionPattern); + LinkedHashMap aMap = new LinkedHashMap<>(); aMap.put(PatternType.MENTION_LONG, mentionLongPattern); + aMap.put(PatternType.MENTION, mentionPattern); aMap.put(PatternType.TAG, hashtagPattern); aMap.put(PatternType.GROUP, groupPattern); patternHashMap = Collections.unmodifiableMap(aMap); 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 c164ae352..19feda165 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -105,7 +105,7 @@ public class SpannableHelper { } if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains(" 0) { //It's a toot + CrossActionHelper.fetchRemoteStatus(context, currentAccount, url, new CrossActionHelper.Callback() { + @Override + public void federatedStatus(Status status) { + Intent intent = new Intent(context, ContextActivity.class); + intent.putExtra(Helper.ARG_STATUS, status); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + + @Override + public void federatedAccount(Account account) { + } + }); + } else {//It's an account + CrossActionHelper.fetchRemoteAccount(context, currentAccount, status.account, new CrossActionHelper.Callback() { + @Override + public void federatedStatus(Status status) { + } + + @Override + public void federatedAccount(Account account) { + Intent intent = new Intent(context, ProfileActivity.class); + Bundle b = new Bundle(); + b.putSerializable(Helper.ARG_ACCOUNT, account); + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + }); + } + } else { + Helper.openBrowser(context, newURL); + } + } @Override From 61ddd2a22d31ea368180177b1fc06a33787f76da Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 15 Jul 2022 17:34:19 +0200 Subject: [PATCH 25/50] some fixes --- .../app/fedilab/android/activities/ProfileActivity.java | 4 ++-- .../fedilab/android/client/entities/api/Announcement.java | 2 +- .../fedilab/android/ui/drawer/NotificationAdapter.java | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java index 5bfdabab6..a40c50ad4 100644 --- a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java @@ -364,7 +364,7 @@ public class ProfileActivity extends BaseActivity { } else { Spannable textAccount = account.span_display_name != null ? account.span_display_name : new SpannableString(account.display_name); CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textAccount, binding.accountDn, null, id -> { - binding.accountDn.invalidate(); + binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); }); binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); } @@ -386,7 +386,7 @@ public class ProfileActivity extends BaseActivity { textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)); else textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)); - CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textNote, binding.accountNote, null, id -> binding.accountNote.invalidate()); + CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textNote, binding.accountNote, null, id -> binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE)); binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java index 287e245ee..a642040c6 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java @@ -51,5 +51,5 @@ public class Announcement { //Some extra spannable element - They will be filled automatically when fetching the status public transient Spannable span_content; - public boolean emojiFetched = false; + public transient boolean emojiFetched = false; } 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 86bcf4a22..27150f845 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 @@ -197,13 +197,13 @@ public class NotificationAdapter extends RecyclerView.Adapter { + CustomEmoji.displayEmoji(context, notification.account.emojis, title, holderStatus.binding.displayName, notification.id, id -> { if (!notification.account.emojiFetched) { notification.account.emojiFetched = true; holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); From 27b05d5b6bef79c2bcea6e9b09022e4857106724 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 15 Jul 2022 18:12:29 +0200 Subject: [PATCH 26/50] Fix issue #248 - nsfw no respected when posting --- .../android/activities/ComposeActivity.java | 26 ++++++++++++------- .../android/ui/drawer/ComposeAdapter.java | 17 +++++++----- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java index dc06ddcfc..ceea97968 100644 --- a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java @@ -103,7 +103,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana public static final int REQUEST_AUDIO_PERMISSION_RESULT = 1653; public static final int PICK_MEDIA = 5700; public static final int TAKE_PHOTO = 5600; - + private final Timer timer = new Timer(); private List statusList; private Status statusReply, statusMention; private StatusDraft statusDraft; @@ -353,12 +353,14 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana .registerReceiver(imageReceiver, new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE)); - new Timer().scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - storeDraft(false); - } - }, 0, 10000); + if (timer != null) { + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + storeDraft(false); + } + }, 0, 10000); + } if (sharedUriList != null && sharedUriList.size() > 0) { @@ -393,6 +395,9 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana @Override protected void onDestroy() { super.onDestroy(); + if (timer != null) { + timer.cancel(); + } LocalBroadcastManager.getInstance(this) .unregisterReceiver(imageReceiver); } @@ -665,6 +670,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } else { statusReplies.add(status); } + } if (statusDraft == null) { statusDraft = new StatusDraft(ComposeActivity.this); @@ -675,10 +681,12 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } } if (statusReplies.size() > 0) { - statusDraft.statusReplyList = statusReplies; + statusDraft.statusReplyList = new ArrayList<>(); + statusDraft.statusReplyList.addAll(statusReplies); } if (statusDrafts.size() > 0) { - statusDraft.statusDraftList = statusDrafts; + statusDraft.statusDraftList = new ArrayList<>(); + statusDraft.statusDraftList.addAll(statusDrafts); } if (statusDraft.instance == null) { statusDraft.instance = account.instance; 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 2ce9f3fa5..34509b108 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 @@ -456,13 +456,18 @@ public class ComposeAdapter extends RecyclerView.Adapter attachmentList = statusList.get(position).media_attachments; if (attachmentList != null && attachmentList.size() > 0) { holder.binding.sensitiveMedia.setVisibility(View.VISIBLE); - if (currentAccount.mastodon_account.source != null) { - holder.binding.sensitiveMedia.setChecked(currentAccount.mastodon_account.source.sensitive); - statusList.get(position).sensitive = currentAccount.mastodon_account.source.sensitive; - } else { - statusList.get(position).sensitive = false; + if (!statusList.get(position).sensitive) { + if (currentAccount.mastodon_account.source != null) { + holder.binding.sensitiveMedia.setChecked(currentAccount.mastodon_account.source.sensitive); + statusList.get(position).sensitive = currentAccount.mastodon_account.source.sensitive; + } else { + statusList.get(position).sensitive = false; + } } - holder.binding.sensitiveMedia.setOnCheckedChangeListener((buttonView, isChecked) -> statusList.get(position).sensitive = isChecked); + + holder.binding.sensitiveMedia.setOnCheckedChangeListener((buttonView, isChecked) -> { + statusList.get(position).sensitive = isChecked; + }); int mediaPosition = 0; for (Attachment attachment : attachmentList) { ComposeAttachmentItemBinding composeAttachmentItemBinding = ComposeAttachmentItemBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false); From b360a9a82700af8bd7aafcd479303670cadaba3f Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 15 Jul 2022 18:18:53 +0200 Subject: [PATCH 27/50] Fix an issue with media not hidden --- .../main/java/app/fedilab/android/ui/drawer/StatusAdapter.java | 2 -- 1 file changed, 2 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 2069205ff..866143e03 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 @@ -1060,7 +1060,6 @@ public class StatusAdapter extends RecyclerView.Adapter .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)))) - .apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))) .into(layoutMediaBinding.media); } layoutMediaBinding.viewHide.setOnClickListener(v -> { @@ -1112,7 +1111,6 @@ public class StatusAdapter extends RecyclerView.Adapter Glide.with(layoutMediaBinding.media.getContext()) .load(attachment.preview_url) .apply(new RequestOptions().transform(new BlurTransformation(50, 3))) - .apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))) // .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) .into(layoutMediaBinding.media); } From dc1155d7c25b197bb9450b35eb8999239c4fa823 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 15 Jul 2022 18:27:44 +0200 Subject: [PATCH 28/50] Release 3.0.7 --- app/build.gradle | 4 +-- .../app/fedilab/android/BaseMainActivity.java | 2 +- .../app/fedilab/android/helper/Helper.java | 33 ++++++++++++++----- app/src/main/res/layout/nav_header_main.xml | 4 +-- .../metadata/android/en/changelogs/397.txt | 1 + 5 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 src/fdroid/fastlane/metadata/android/en/changelogs/397.txt diff --git a/app/build.gradle b/app/build.gradle index b41919ee1..82f5e216c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { defaultConfig { minSdk 21 targetSdk 31 - versionCode 396 - versionName "3.0.6" + versionCode 397 + versionName "3.0.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions "default" diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index 2c9218a02..49c3f744d 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -772,7 +772,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt currentAccount.mastodon_account.display_name = currentAccount.mastodon_account.acct; } headerMainBinding.accountName.setText(currentAccount.mastodon_account.display_name); - Helper.loadPP(headerMainBinding.accountProfilePicture, currentAccount); + Helper.loadPP(headerMainBinding.accountProfilePicture, currentAccount, false); MastodonHelper.loadProfileMediaMastodon(headerMainBinding.backgroundImage, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER); /* * Some general data are loaded when the app starts such; 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 c3111a67d..b85d10fe6 100644 --- a/app/src/main/java/app/fedilab/android/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/helper/Helper.java @@ -87,8 +87,10 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.load.resource.gif.GifDrawable; import com.bumptech.glide.request.RequestOptions; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -1043,6 +1045,7 @@ public class Helper { } + /** * Load a profile picture for the account * @@ -1050,25 +1053,39 @@ public class Helper { * @param account - {@link Account} */ public static void loadPP(ImageView view, BaseAccount account) { + loadPP(view, account, false); + } + + /** + * Load a profile picture for the account + * + * @param view ImageView - the view where the image will be loaded + * @param account - {@link Account} + */ + public static void loadPP(ImageView view, BaseAccount account, boolean crop) { Context context = view.getContext(); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false); String targetedUrl = disableGif ? account.mastodon_account.avatar_static : account.mastodon_account.avatar; if (targetedUrl != null) { if (disableGif || (!targetedUrl.endsWith(".gif"))) { - Glide.with(view.getContext()) + RequestBuilder requestBuilder = Glide.with(view.getContext()) .asDrawable() .load(targetedUrl) - .thumbnail(0.1f) - .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) - .into(view); + .thumbnail(0.1f); + if (crop) { + requestBuilder = requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))); + } + requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))).into(view); } else { - Glide.with(view.getContext()) + RequestBuilder requestBuilder = Glide.with(view.getContext()) .asGif() .load(targetedUrl) - .thumbnail(0.1f) - .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) - .into(view); + .thumbnail(0.1f); + if (crop) { + requestBuilder = requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))); + } + requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))).into(view); } } else { Glide.with(view.getContext()) diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml index 3676b0ce0..efe0f42a5 100644 --- a/app/src/main/res/layout/nav_header_main.xml +++ b/app/src/main/res/layout/nav_header_main.xml @@ -45,8 +45,8 @@ android:layout_gravity="center_vertical" android:contentDescription="@string/profile_picture" android:paddingTop="@dimen/nav_header_vertical_spacing" - tools:src="@tools:sample/avatars" - android:scaleType="fitCenter" /> + android:scaleType="fitCenter" + tools:src="@tools:sample/avatars" /> Date: Sat, 16 Jul 2022 09:21:43 +0200 Subject: [PATCH 29/50] some fixes --- .../android/activities/ContextActivity.java | 12 ++----- .../android/ui/drawer/StatusAdapter.java | 10 ++---- .../timeline/FragmentMastodonContext.java | 34 +++++++------------ .../timeline/FragmentMastodonTimeline.java | 12 ++----- 4 files changed, 21 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java index 61732dcfb..28aff52a9 100644 --- a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java @@ -88,15 +88,9 @@ public class ContextActivity extends BaseActivity { } MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account); Bundle bundle = new Bundle(); - new Thread(() -> { - focusedStatus = SpannableHelper.convertStatus(getApplication().getApplicationContext(), focusedStatus); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - bundle.putSerializable(Helper.ARG_STATUS, focusedStatus); - currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null); - }; - mainHandler.post(myRunnable); - }).start(); + focusedStatus = SpannableHelper.convertStatus(getApplication().getApplicationContext(), focusedStatus); + bundle.putSerializable(Helper.ARG_STATUS, focusedStatus); + currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null); StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class); timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> { if (status != null) { 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 866143e03..a95a4d297 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 @@ -35,8 +35,6 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.os.CountDownTimer; -import android.os.Handler; -import android.os.Looper; import android.text.Html; import android.text.Spannable; import android.text.SpannableString; @@ -1628,12 +1626,8 @@ public class StatusAdapter extends RecyclerView.Adapter if (translate.getTranslatedContent() != null) { statusToDeal.translationShown = true; statusToDeal.translationContent = translate.getTranslatedContent(); - new Thread(() -> { - SpannableHelper.convertStatus(context.getApplicationContext(), statusToDeal); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal)); - mainHandler.post(myRunnable); - }).start(); + SpannableHelper.convertStatus(context.getApplicationContext(), statusToDeal); + adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal)); } else { Toasty.error(context, context.getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java index a807bcc95..e4be47a13 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java @@ -21,8 +21,6 @@ import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -98,26 +96,20 @@ public class FragmentMastodonContext extends Fragment { } } else if (statusPosted != null && statusAdapter != null) { if (requireActivity() instanceof ContextActivity) { - new Thread(() -> { - Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - int i = 0; - for (Status status : statuses) { - if (status.id.equals(convertStatus.in_reply_to_id)) { - statuses.add((i + 1), convertStatus); - statusAdapter.notifyItemInserted((i + 1)); - if (requireActivity() instanceof ContextActivity) { - //Redraw decorations - statusAdapter.notifyItemRangeChanged(0, statuses.size()); - } - break; - } - i++; + Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); + int i = 0; + for (Status status : statuses) { + if (status.id.equals(convertStatus.in_reply_to_id)) { + statuses.add((i + 1), convertStatus); + statusAdapter.notifyItemInserted((i + 1)); + if (requireActivity() instanceof ContextActivity) { + //Redraw decorations + statusAdapter.notifyItemRangeChanged(0, statuses.size()); } - }; - mainHandler.post(myRunnable); - }).start(); + break; + } + i++; + } } } } diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index b499fb7a8..d62864015 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -118,15 +118,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. statusAdapter.notifyItemRemoved(position); } } else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) { - new Thread(() -> { - Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> { - statuses.add(0, convertStatus); - statusAdapter.notifyItemInserted(0); - }; - mainHandler.post(myRunnable); - }).start(); + Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); + statuses.add(0, convertStatus); + statusAdapter.notifyItemInserted(0); } } } From dd924cba2021be7b0c4439be54492a3d0848a6dc Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 11:43:23 +0200 Subject: [PATCH 30/50] some tries --- .../activities/AdminAccountActivity.java | 18 +- .../activities/AdminReportActivity.java | 19 +- .../android/activities/ComposeActivity.java | 5 - .../android/activities/ContextActivity.java | 2 - .../android/activities/ProfileActivity.java | 43 +- .../android/client/entities/api/Account.java | 36 +- .../android/client/entities/api/Poll.java | 15 + .../android/client/entities/api/Status.java | 53 +- .../client/entities/app/QuickLoad.java | 2 - .../android/helper/CrossActionHelper.java | 14 +- .../android/helper/NotificationsHelper.java | 5 - .../android/helper/SpannableHelper.java | 960 +++++------------- .../android/ui/drawer/AccountAdapter.java | 26 +- .../android/ui/drawer/AccountListAdapter.java | 7 +- .../android/ui/drawer/ComposeAdapter.java | 32 +- .../ui/drawer/ConversationAdapter.java | 26 +- .../ui/drawer/NotificationAdapter.java | 81 +- .../android/ui/drawer/StatusAdapter.java | 182 ++-- .../timeline/FragmentMastodonContext.java | 6 +- .../timeline/FragmentMastodonTimeline.java | 4 +- .../viewmodel/mastodon/AccountsVM.java | 25 +- .../viewmodel/mastodon/AnnouncementsVM.java | 2 - .../viewmodel/mastodon/NotificationsVM.java | 13 - .../android/viewmodel/mastodon/SearchVM.java | 6 - .../viewmodel/mastodon/StatusesVM.java | 33 +- .../viewmodel/mastodon/TimelinesVM.java | 30 +- 26 files changed, 567 insertions(+), 1078 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java index 8421d924a..94e63ecfc 100644 --- a/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java @@ -22,8 +22,6 @@ import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -46,6 +44,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -105,14 +104,7 @@ public class AdminAccountActivity extends BaseActivity { } binding.toolbar.setPopupTheme(Helper.popupStyle()); if (account != null) { - new Thread(() -> { - account = SpannableHelper.convertAccount(AdminAccountActivity.this, account); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> initializeView(account); - mainHandler.post(myRunnable); - - }).start(); - + initializeView(account); } else { Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); finish(); @@ -314,7 +306,11 @@ public class AdminAccountActivity extends BaseActivity { } - binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE); + binding.accountDn.setText( + account.getSpanDisplayName(AdminAccountActivity.this, + new WeakReference<>(binding.accountDn), + id -> binding.accountDn.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); diff --git a/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java index 701dbd07f..4feea554c 100644 --- a/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java @@ -22,8 +22,6 @@ import android.content.SharedPreferences; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -46,6 +44,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -105,14 +104,7 @@ public class AdminReportActivity extends BaseActivity { } binding.toolbar.setPopupTheme(Helper.popupStyle()); if (account != null) { - new Thread(() -> { - account = SpannableHelper.convertAccount(AdminReportActivity.this, account); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> initializeView(account); - mainHandler.post(myRunnable); - - }).start(); - + initializeView(account); } else { Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); finish(); @@ -331,8 +323,11 @@ public class AdminReportActivity extends BaseActivity { binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); } - - binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE); + binding.accountDn.setText( + account.getSpanDisplayName(AdminReportActivity.this, + new WeakReference<>(binding.accountDn), + id -> binding.accountDn.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); diff --git a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java index ceea97968..050ce0095 100644 --- a/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ComposeActivity.java @@ -84,7 +84,6 @@ import app.fedilab.android.helper.DividerDecorationSimple; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MediaHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.interfaces.OnDownloadInterface; import app.fedilab.android.jobs.ScheduleThreadWorker; @@ -268,9 +267,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana } }); } else if (statusDraft != null) {//Restore a draft with all messages - if (statusDraft.statusReplyList != null) { - statusDraft.statusReplyList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusDraft.statusReplyList); - } if (statusDraft.statusReplyList != null) { statusList.addAll(statusDraft.statusReplyList); binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList)); @@ -285,7 +281,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1); } else if (statusReply != null) { - statusReply = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusReply); statusList.add(statusReply); int statusCount = statusList.size(); statusDraftList.get(0).in_reply_to_id = statusReply.id; diff --git a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java index 28aff52a9..0287e4d90 100644 --- a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java @@ -43,7 +43,6 @@ import app.fedilab.android.databinding.ActivityConversationBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext; import app.fedilab.android.viewmodel.mastodon.StatusesVM; @@ -88,7 +87,6 @@ public class ContextActivity extends BaseActivity { } MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account); Bundle bundle = new Bundle(); - focusedStatus = SpannableHelper.convertStatus(getApplication().getApplicationContext(), focusedStatus); bundle.putSerializable(Helper.ARG_STATUS, focusedStatus); currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null); StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class); diff --git a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java index a40c50ad4..7371dbbdf 100644 --- a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java @@ -27,12 +27,7 @@ import android.content.SharedPreferences; import android.content.res.ColorStateList; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.text.Html; -import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; import android.text.method.LinkMovementMethod; @@ -65,6 +60,7 @@ import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import com.google.android.material.tabs.TabLayout; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -90,7 +86,6 @@ import app.fedilab.android.client.entities.app.WellKnownNodeinfo; import app.fedilab.android.databinding.ActivityProfileBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.CrossActionHelper; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.SpannableHelper; @@ -165,14 +160,7 @@ public class ProfileActivity extends BaseActivity { binding.toolbar.setPopupTheme(Helper.popupStyle()); accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class); if (account != null) { - new Thread(() -> { - account = SpannableHelper.convertAccount(ProfileActivity.this, account); - Handler mainHandler = new Handler(Looper.getMainLooper()); - Runnable myRunnable = () -> initializeView(account); - mainHandler.post(myRunnable); - - }).start(); - + initializeView(account); } else if (account_id != null) { accountsVM.getAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account_id).observe(ProfileActivity.this, fetchedAccount -> { account = fetchedAccount; @@ -359,15 +347,12 @@ public class ProfileActivity extends BaseActivity { binding.fieldsContainer.setAdapter(fieldAdapter); binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this)); } - if (account.span_display_name == null && account.display_name == null) { - binding.accountDn.setText(account.username); - } else { - Spannable textAccount = account.span_display_name != null ? account.span_display_name : new SpannableString(account.display_name); - CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textAccount, binding.accountDn, null, id -> { - binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); - }); - binding.accountDn.setText(textAccount, TextView.BufferType.SPANNABLE); - } + + binding.accountDn.setText( + account.getSpanDisplayName(ProfileActivity.this, + new WeakReference<>(binding.accountDn), + id -> binding.accountDn.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { @@ -381,13 +366,11 @@ public class ProfileActivity extends BaseActivity { clipboard.setPrimaryClip(clip); return false; }); - Spannable textNote; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)); - else - textNote = account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)); - CustomEmoji.displayEmoji(ProfileActivity.this, account.emojis, textNote, binding.accountNote, null, id -> binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE)); - binding.accountNote.setText(textNote, TextView.BufferType.SPANNABLE); + binding.accountNote.setText( + account.getSpanNote(ProfileActivity.this, + new WeakReference<>(binding.accountNote), + id -> binding.accountNote.invalidate()), + TextView.BufferType.SPANNABLE); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); MastodonHelper.loadPPMastodon(binding.accountPp, account); diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java index 012314ab7..5450e7a26 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java @@ -14,15 +14,20 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.view.View; import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; +import app.fedilab.android.helper.SpannableHelper; + public class Account implements Serializable { @SerializedName("id") @@ -74,11 +79,36 @@ public class Account implements Serializable { @SerializedName("moved") public Account moved; + public transient boolean emojiDisplayNameFetched = false; + public transient boolean emojiNoteFetched = false; //Some extra spannable element - They will be filled automatically when fetching the account - public transient Spannable span_display_name; - public transient Spannable span_note; + private transient Spannable span_display_name; + private transient Spannable span_note; + + public synchronized Spannable getSpanDisplayName(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_display_name != null) { + return span_display_name; + } + if (display_name == null) { + display_name = username; + } + span_display_name = SpannableHelper.convert(context, display_name, null, this, true, viewWeakReference, !emojiDisplayNameFetched ? callback : null); + emojiDisplayNameFetched = true; + return span_display_name; + } + + + public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_note != null) { + return span_note; + } + span_note = SpannableHelper.convert(context, note, null, this, true, viewWeakReference, !emojiNoteFetched ? callback : null); + emojiNoteFetched = true; + return span_note; + } + public transient RelationShip relationShip; - public transient boolean emojiFetched = false; + public static class AccountParams implements Serializable { @SerializedName("discoverable") diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java index a0d80fbea..7df3e7c03 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java @@ -14,14 +14,19 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.view.View; import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; +import app.fedilab.android.helper.SpannableHelper; + public class Poll implements Serializable { @SerializedName("id") @@ -55,5 +60,15 @@ public class Poll implements Serializable { //Some extra spannable element - They will be filled automatically when fetching the poll public transient Spannable span_title; + public transient boolean emojiTitleFetched = false; + + public Spannable getSpanTitle(Context context, Status status, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_title != null) { + return span_title; + } + span_title = SpannableHelper.convert(context, title, status, null, true, viewWeakReference, !emojiTitleFetched ? callback : null); + emojiTitleFetched = true; + return span_title; + } } } 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 5224dca53..159feea2d 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 @@ -14,16 +14,21 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.view.View; import androidx.annotation.NonNull; import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; +import app.fedilab.android.helper.SpannableHelper; + public class Status implements Serializable, Cloneable { @SerializedName("id") @@ -88,10 +93,9 @@ public class Status implements Serializable, Cloneable { public Attachment art_attachment; - //Some extra spannable element - They will be filled automatically when fetching the status - public transient Spannable span_content; - public transient Spannable span_spoiler_text; - public transient Spannable span_translate; + public transient boolean emojiContentFetched = false; + public transient boolean emojiSpoilerFetched = false; + public transient boolean emojiTranslateFetched = false; public boolean isExpended = false; public boolean isTruncated = true; public boolean isFetchMore = false; @@ -105,7 +109,46 @@ public class Status implements Serializable, Cloneable { public transient boolean setCursorToEnd = false; public transient int cursorPosition = 0; public transient boolean submitted = false; - public transient boolean emojiFetched = false; + //Some extra spannable element - They will be filled automatically when fetching the status + private transient Spannable span_content; + private transient Spannable span_spoiler_text; + private transient Spannable span_translate; + + public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_content != null) { + return span_content; + } + span_content = SpannableHelper.convert(context, content, this, null, true, viewWeakReference, !emojiContentFetched ? callback : null); + emojiContentFetched = true; + return span_content; + } + + + public Spannable getSpanContentNitter() { + if (span_content != null) { + return span_content; + } + span_content = SpannableHelper.convertNitter(content); + return span_content; + } + + public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_spoiler_text != null) { + return span_spoiler_text; + } + span_spoiler_text = SpannableHelper.convert(context, spoiler_text, this, null, true, viewWeakReference, !emojiSpoilerFetched ? callback : null); + emojiSpoilerFetched = true; + return span_spoiler_text; + } + + public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { + if (span_translate != null) { + return span_translate; + } + span_translate = SpannableHelper.convert(context, translationContent, this, null, true, viewWeakReference, !emojiTranslateFetched ? callback : null); + emojiTranslateFetched = true; + return span_translate; + } @NonNull public Object clone() throws CloneNotSupportedException { diff --git a/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java b/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java index d3f56ce41..26140deba 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java +++ b/app/src/main/java/app/fedilab/android/client/entities/app/QuickLoad.java @@ -29,7 +29,6 @@ import app.fedilab.android.client.entities.api.Notification; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.sqlite.Sqlite; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonNotification; @@ -576,7 +575,6 @@ public class QuickLoad { } quickLoad.position = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_POSITION)); //TimelineHelper.filterStatus(_mContext, quickLoad.statuses, TimelineHelper.FilterTimeLineType.PUBLIC); - quickLoad.statuses = SpannableHelper.convertStatus(_mContext, quickLoad.statuses); return quickLoad; } diff --git a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java index 48cea1a0b..97b7fdc10 100644 --- a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java @@ -278,8 +278,6 @@ public class CrossActionHelper { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(context, results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); @@ -308,15 +306,15 @@ public class CrossActionHelper { /** * Fetch and federate the remote status */ - public static void fetchRemoteAccount(@NonNull Context context, @NonNull BaseAccount ownerAccount, app.fedilab.android.client.entities.api.Account targetedAccount, Callback callback) { + public static void fetchRemoteAccount(@NonNull Context context, @NonNull BaseAccount ownerAccount, String targetedAcct, Callback callback) { MastodonSearchService mastodonSearchService = init(context, BaseMainActivity.currentInstance); String search; - if (targetedAccount.acct.contains("@")) { //Not from same instance - search = targetedAccount.acct; + if (targetedAcct.contains("@")) { //Not from same instance + search = targetedAcct; } else { - search = targetedAccount.acct + "@" + BaseMainActivity.currentInstance; + search = targetedAcct + "@" + BaseMainActivity.currentInstance; } new Thread(() -> { Call resultsCall = mastodonSearchService.search(ownerAccount.token, search, null, "accounts", false, true, false, 0, null, null, 1); @@ -329,8 +327,6 @@ public class CrossActionHelper { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(context, results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); @@ -374,8 +370,6 @@ public class CrossActionHelper { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(context, results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); diff --git a/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java b/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java index 60b3b7c1d..5fb0e16e6 100644 --- a/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/NotificationsHelper.java @@ -115,11 +115,6 @@ public class NotificationsHelper { if (notifications.notifications.size() > 0) { since_ids.put(slug, notifications.notifications.get(0).id); } - for (Notification notification : notifications.notifications) { - if (notification != null && notification.status != null) { - notification.status = SpannableHelper.convertStatus(context.getApplicationContext(), notification.status); - } - } } notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers()); } 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 19feda165..39fec6469 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -24,6 +24,9 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -35,16 +38,25 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.style.ClickableSpan; +import android.text.style.ImageSpan; import android.text.style.URLSpan; +import android.util.Log; import android.util.Patterns; import android.view.LayoutInflater; import android.view.View; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceManager; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.transition.Transition; import java.io.IOException; +import java.lang.ref.WeakReference; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -62,11 +74,9 @@ import app.fedilab.android.activities.ContextActivity; import app.fedilab.android.activities.HashTagActivity; import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.client.entities.api.Account; -import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Attachment; -import app.fedilab.android.client.entities.api.Field; +import app.fedilab.android.client.entities.api.Emoji; import app.fedilab.android.client.entities.api.Mention; -import app.fedilab.android.client.entities.api.Poll; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.databinding.PopupLinksBinding; import es.dmoral.toasty.Toasty; @@ -75,150 +85,143 @@ public class SpannableHelper { public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; + public static Spannable convert(Context context, String text, + Status status, Account account, + boolean convertHtml, + WeakReference viewWeakReference, + EmojiCallback listener) { - private static Spannable convert(@NonNull Context context, @NonNull Status status, String text) { - return convert(context, status, text, true); - } - /** - * Convert HTML content to text. Also, it handles click on link - * This needs to be run asynchronously - * - * @param context {@link Context} - * @param status {@link Status} - Status concerned by the spannable transformation - * @param text String - text to convert, it can be content, spoiler, poll items, etc. - * @param convertHtml boolean - text need to be converted in html first - * @return Spannable string - */ - private static Spannable convert(@NonNull Context context, @NonNull Status status, String text, boolean convertHtml) { SpannableString initialContent; if (text == null) { return null; } - Matcher matcherALink = Helper.aLink.matcher(text); - //We stock details - HashMap urlDetails = new HashMap<>(); - while (matcherALink.find()) { - String urlText = matcherALink.group(3); - String url = matcherALink.group(2); - if (urlText != null) { - urlText = urlText.substring(1); - } - if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains(" mentionList = null; + List emojiList = null; + if (status != null) { + mentionList = status.mentions; + emojiList = status.emojis; + id = status.id; + } else if (account != null) { + emojiList = account.emojis; + id = account.id; } - if(convertHtml) { + HashMap urlDetails = new HashMap<>(); + if (convertHtml) { + Matcher matcherALink = Helper.aLink.matcher(text); + //We stock details + while (matcherALink.find()) { + String urlText = matcherALink.group(3); + String url = matcherALink.group(2); + if (urlText != null) { + urlText = urlText.substring(1); + } + if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains("= Build.VERSION_CODES.N) initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); else initialContent = new SpannableString(Html.fromHtml(text)); + + content = new SpannableStringBuilder(initialContent); + URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); + for (URLSpan span : urls) { + content.removeSpan(span); + } + //Make tags, mentions, groups + interaction(context, content, mentionList); + //Make all links + linkify(context, content, urlDetails); } else { - initialContent = new SpannableString(text); + content = new SpannableStringBuilder(text); } - - SpannableStringBuilder content = new SpannableStringBuilder(initialContent); - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); - for (URLSpan span : urls) { - content.removeSpan(span); - } - - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - String word = content.toString().substring(matchStart, matchEnd); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - Mention targetedMention = null; - HashMap countUsername = new HashMap<>(); - - for (Mention mention : status.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (countUsername.containsKey(mention.username)) { - countUsername.put(mention.username, count + 1); - } else { - countUsername.put(mention.username, 1); + if (emojiList != null && emojiList.size() > 0) { + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); + boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); + int count = 1; + for (Emoji emoji : emojiList) { + int finalCount = count; + List finalEmojiList = emojiList; + String finalId = id; + List finalEmojiList1 = emojiList; + Glide.with(context) + .asDrawable() + .load(animate ? emoji.url : emoji.static_url) + .into( + new CustomTarget() { + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (finalCount == finalEmojiList.size() && listener != null) { + listener.transformationDone(finalId); } } - for (Mention mention : status.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; + + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) + .matcher(content); + while (matcher.find()) { + ImageSpan imageSpan; + resource.setBounds(0, 0, (int) Helper.convertDpToPixel(20, context), (int) Helper.convertDpToPixel(20, context)); + resource.setVisible(true, true); + imageSpan = new ImageSpan(resource); + content.setSpan( + imageSpan, matcher.start(), + matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } - if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { - targetedMention = mention; - break; + if (animate && resource instanceof Animatable) { + Drawable.Callback callback = resource.getCallback(); + resource.setCallback(new Drawable.Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable drawable) { + if (callback != null) { + callback.invalidateDrawable(drawable); + } + view.invalidate(); + } + + @Override + public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { + if (callback != null) { + callback.scheduleDrawable(drawable, runnable, l); + } + } + + @Override + public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { + if (callback != null) { + callback.unscheduleDrawable(drawable, runnable); + } + } + }); + ((Animatable) resource).start(); + + } + Log.v(Helper.TAG, ">: " + emoji.shortcode + " --> " + listener + " - " + finalCount + " <> " + finalEmojiList1.size()); + if (finalCount == finalEmojiList.size() && listener != null) { + Log.v(Helper.TAG, "OK FOR: " + emoji.shortcode + " --> " + finalId); + listener.transformationDone(finalId); } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case MENTION_LONG: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - targetedMention = null; - for (Mention mention : status.mentions) { - if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { - targetedMention = mention; - break; - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } + } + } + ); + count++; } } + return trimSpannable(new SpannableStringBuilder(content)); + } + + private static void linkify(Context context, SpannableStringBuilder content, HashMap urlDetails) { //--- URLs ---- Matcher matcherLink = Patterns.WEB_URL.matcher(content); int offSetTruncate = 0; @@ -418,7 +421,7 @@ public class SpannableHelper { } }); } else {//It's an account - CrossActionHelper.fetchRemoteAccount(context, currentAccount, status.account, new CrossActionHelper.Callback() { + CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() { @Override public void federatedStatus(Status status) { } @@ -449,8 +452,126 @@ public class SpannableHelper { }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } } + } + private static void interaction(Context context, Spannable content, List mentions) { + // --- For all patterns defined in Helper class --- + for (Map.Entry entry : Helper.patternHashMap.entrySet()) { + Helper.PatternType patternType = entry.getKey(); + Pattern pattern = entry.getValue(); + Matcher matcher = pattern.matcher(content); + if (pattern == Helper.mentionPattern && mentions == null) { + continue; + } else if (pattern == Helper.mentionLongPattern && mentions == null) { + continue; + } + while (matcher.find()) { + int matchStart = matcher.start(); + int matchEnd = matcher.end(); + String word = content.toString().substring(matchStart, matchEnd); + if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { + URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); + content.removeSpan(span); + content.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View textView) { + textView.setTag(CLICKABLE_SPAN); + switch (patternType) { + case TAG: + Intent intent = new Intent(context, HashTagActivity.class); + Bundle b = new Bundle(); + b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + case GROUP: + break; + case MENTION: + intent = new Intent(context, ProfileActivity.class); + b = new Bundle(); + Mention targetedMention = null; + HashMap countUsername = new HashMap<>(); + + if (mentions != null) { + for (Mention mention : mentions) { + Integer count = countUsername.get(mention.username); + if (count == null) { + count = 0; + } + if (countUsername.containsKey(mention.username)) { + countUsername.put(mention.username, count + 1); + } else { + countUsername.put(mention.username, 1); + } + } + for (Mention mention : mentions) { + Integer count = countUsername.get(mention.username); + if (count == null) { + count = 0; + } + if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { + targetedMention = mention; + break; + } + } + } + if (targetedMention != null) { + b.putString(Helper.ARG_USER_ID, targetedMention.id); + } else { + b.putString(Helper.ARG_MENTION, word.trim()); + } + + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + case MENTION_LONG: + intent = new Intent(context, ProfileActivity.class); + b = new Bundle(); + targetedMention = null; + if (mentions != null) { + for (Mention mention : mentions) { + if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { + targetedMention = mention; + break; + } + } + } + if (targetedMention != null) { + b.putString(Helper.ARG_USER_ID, targetedMention.id); + } else { + b.putString(Helper.ARG_MENTION, word.trim()); + } + intent.putExtras(b); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + break; + } + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + ds.setColor(linkColor); + } + }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } + } + + /** + * Convert HTML content to text. Also, it handles click on link + * This needs to be run asynchronously + * + * @param status {@link Status} - Status concerned by the spannable transformation + * @param content String - text to convert, it can be content, spoiler, poll items, etc. + * @return Spannable string + */ + private static void convertOuich(@NonNull Status status, SpannableStringBuilder content) { Matcher matcher = Helper.ouichesPattern.matcher(content); while (matcher.find()) { @@ -477,361 +598,28 @@ public class SpannableHelper { status.media_attachments.add(attachment); } } - return trimSpannable(new SpannableStringBuilder(content)); } - /** * Convert HTML content to text. Also, it handles click on link * This needs to be run asynchronously * - * @param context {@link Context} - * @param announcement {@link Announcement} - Announcement concerned by the spannable transformation - * @param text String - text to convert, it can be content, spoiler, poll items, etc. + * @param text String - text to convert, it can be content, spoiler, poll items, etc. * @return Spannable string */ - private static Spannable convert(@NonNull Context context, @NonNull Announcement announcement, String text) { + public static Spannable convertNitter(String text) { SpannableString initialContent; if (text == null) { return null; } - Matcher matcherALink = Helper.aLink.matcher(text); - //We stock details - HashMap urlDetails = new HashMap<>(); - while (matcherALink.find()) { - String urlText = matcherALink.group(3); - String url = matcherALink.group(2); - if (urlText != null) { - urlText = urlText.substring(1); - } - if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains("= Build.VERSION_CODES.N) initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); else initialContent = new SpannableString(Html.fromHtml(text)); - - SpannableStringBuilder content = new SpannableStringBuilder(initialContent); - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); - for (URLSpan span : urls) { - content.removeSpan(span); - } - - //--- URLs ---- - Matcher matcherLink = Patterns.WEB_URL.matcher(content); - int offSetTruncate = 0; - while (matcherLink.find()) { - int matchStart = matcherLink.start() - offSetTruncate; - int matchEnd = matchStart + matcherLink.group().length(); - if (matchEnd > content.toString().length()) { - matchEnd = content.toString().length(); - } - - if (content.toString().length() < matchEnd || matchStart < 0 || matchStart > matchEnd) { - continue; - } - final String url = content.toString().substring(matchStart, matchEnd); - String newURL = Helper.transformURL(context, url); - //If URL has been transformed - if (newURL.compareTo(url) != 0) { - content.replace(matchStart, matchEnd, newURL); - offSetTruncate += (newURL.length() - url.length()); - matchEnd = matchStart + newURL.length(); - //The transformed URL was in the list of URLs having a different names - if (urlDetails.containsKey(url)) { - urlDetails.put(newURL, urlDetails.get(url)); - } - } - //Truncate URL if needed - //TODO: add an option to disable truncated URLs - String urlText = newURL; - if (newURL.length() > 30 && !urlDetails.containsKey(urlText)) { - urlText = urlText.substring(0, 30); - urlText += "…"; - content.replace(matchStart, matchEnd, urlText); - matchEnd = matchStart + 31; - offSetTruncate += (newURL.length() - urlText.length()); - } else if (urlDetails.containsKey(urlText) && urlDetails.get(urlText) != null) { - urlText = urlDetails.get(urlText); - if (urlText != null) { - content.replace(matchStart, matchEnd, urlText); - matchEnd = matchStart + urlText.length(); - offSetTruncate += (newURL.length() - urlText.length()); - } - } - - if (matchEnd <= content.length() && matchEnd >= matchStart) { - content.setSpan(new LongClickableSpan() { - @Override - public void onLongClick(View view) { - Context mContext = view.getContext(); - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext, Helper.dialogStyle()); - PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context)); - dialogBuilder.setView(popupLinksBinding.getRoot()); - AlertDialog alertDialog = dialogBuilder.create(); - alertDialog.show(); - - popupLinksBinding.displayFullLink.setOnClickListener(v -> { - AlertDialog.Builder builder = new AlertDialog.Builder(mContext, Helper.dialogStyle()); - builder.setMessage(url); - builder.setTitle(context.getString(R.string.display_full_link)); - builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()) - .show(); - alertDialog.dismiss(); - }); - popupLinksBinding.shareLink.setOnClickListener(v -> { - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via)); - sendIntent.putExtra(Intent.EXTRA_TEXT, url); - sendIntent.setType("text/plain"); - sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Intent intentChooser = Intent.createChooser(sendIntent, context.getString(R.string.share_with)); - intentChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intentChooser); - alertDialog.dismiss(); - }); - - popupLinksBinding.openOtherApp.setOnClickListener(v -> { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - context.startActivity(intent); - } catch (Exception e) { - Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show(); - } - alertDialog.dismiss(); - }); - - popupLinksBinding.copyLink.setOnClickListener(v -> { - ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, url); - if (clipboard != null) { - clipboard.setPrimaryClip(clip); - Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show(); - } - alertDialog.dismiss(); - }); - - popupLinksBinding.checkRedirect.setOnClickListener(v -> { - try { - - URL finalUrlCheck = new URL(url); - new Thread(() -> { - try { - String redirect = null; - HttpsURLConnection httpsURLConnection = (HttpsURLConnection) finalUrlCheck.openConnection(); - httpsURLConnection.setConnectTimeout(10 * 1000); - httpsURLConnection.setRequestProperty("http.keepAlive", "false"); - httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT); - httpsURLConnection.setRequestMethod("HEAD"); - if (httpsURLConnection.getResponseCode() == 301 || httpsURLConnection.getResponseCode() == 302) { - Map> map = httpsURLConnection.getHeaderFields(); - for (Map.Entry> entry : map.entrySet()) { - if (entry.toString().toLowerCase().startsWith("location")) { - Matcher matcher = urlPattern.matcher(entry.toString()); - if (matcher.find()) { - redirect = matcher.group(1); - } - } - } - } - httpsURLConnection.getInputStream().close(); - if (redirect != null && redirect.compareTo(url) != 0) { - URL redirectURL = new URL(redirect); - String host = redirectURL.getHost(); - String protocol = redirectURL.getProtocol(); - if (protocol == null || host == null) { - redirect = null; - } - } - Handler mainHandler = new Handler(context.getMainLooper()); - String finalRedirect = redirect; - Runnable myRunnable = () -> { - AlertDialog.Builder builder1 = new AlertDialog.Builder(view.getContext(), Helper.dialogStyle()); - if (finalRedirect != null) { - builder1.setMessage(context.getString(R.string.redirect_detected, url, finalRedirect)); - builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> { - ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect); - if (clipboard1 != null) { - clipboard1.setPrimaryClip(clip1); - Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show(); - } - dialog.dismiss(); - }); - builder1.setNeutralButton(R.string.share_link, (dialog, which) -> { - Intent sendIntent1 = new Intent(Intent.ACTION_SEND); - sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via)); - sendIntent1.putExtra(Intent.EXTRA_TEXT, url); - sendIntent1.setType("text/plain"); - context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with))); - dialog.dismiss(); - }); - } else { - builder1.setMessage(R.string.no_redirect); - } - builder1.setTitle(context.getString(R.string.check_redirect)); - builder1.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()) - .show(); - - }; - mainHandler.post(myRunnable); - } catch (IOException e) { - e.printStackTrace(); - } - - }).start(); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - - alertDialog.dismiss(); - }); - - } - - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$"); - Matcher matcherLink = link.matcher(url); - if (matcherLink.find() && !url.contains("medium.com")) { - if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot - CrossActionHelper.fetchRemoteStatus(context, currentAccount, url, new CrossActionHelper.Callback() { - @Override - public void federatedStatus(Status status) { - Intent intent = new Intent(context, ContextActivity.class); - intent.putExtra(Helper.ARG_STATUS, status); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - - @Override - public void federatedAccount(Account account) { - - } - }); - } - } else { - Helper.openBrowser(context, newURL); - } - - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - String word = content.toString().substring(matchStart, matchEnd); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - Mention targetedMention = null; - HashMap countUsername = new HashMap<>(); - for (Mention mention : announcement.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (countUsername.containsKey(mention.username)) { - countUsername.put(mention.username, count + 1); - } else { - countUsername.put(mention.username, 1); - } - } - for (Mention mention : announcement.mentions) { - Integer count = countUsername.get(mention.username); - if (count == null) { - count = 0; - } - if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) { - targetedMention = mention; - break; - } - } - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case MENTION_LONG: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - targetedMention = null; - for (Mention mention : announcement.mentions) { - if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) { - targetedMention = mention; - break; - } - } - - if (targetedMention != null) { - b.putString(Helper.ARG_USER_ID, targetedMention.id); - } else { - b.putString(Helper.ARG_MENTION, word.trim()); - } - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - } - return trimSpannable(new SpannableStringBuilder(content)); + return initialContent; } + /** * Remove extra carriage returns at the bottom due to

tags in toots * @@ -856,245 +644,6 @@ public class SpannableHelper { return spannable.delete(0, trimStart).delete(spannable.length() - trimEnd, spannable.length()); } - public static List convertStatus(Context context, List statuses) { - if (statuses != null) { - for (Status status : statuses) { - convertStatus(context, status); - } - } - return statuses; - } - - public static List convertNitterStatus(List statuses) { - if (statuses != null) { - for (Status status : statuses) { - convertNitterStatus(status); - } - } - return statuses; - } - - public static void convertNitterStatus(Status status) { - if (status != null) { - status.span_content = SpannableHelper.convertNitter(status.content); - } - } - - /** - * Convert HTML content to text. Also, it handles click on link - * This needs to be run asynchronously - * - * @param text String - text to convert, it can be content, spoiler, poll items, etc. - * @return Spannable string - */ - private static Spannable convertNitter(String text) { - SpannableString initialContent; - if (text == null) { - return null; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); - else - initialContent = new SpannableString(Html.fromHtml(text)); - return initialContent; - } - - public static List convertAnnouncement(Context context, List announcements) { - if (announcements != null) { - for (Announcement announcement : announcements) { - convertAnnouncement(context, announcement); - } - } - return announcements; - } - - - public static Announcement convertAnnouncement(Context context, Announcement announcement) { - if (announcement != null) { - announcement.span_content = SpannableHelper.convert(context, announcement, announcement.content); - } - return announcement; - } - - public static Status convertStatus(Context context, Status status) { - if (status != null) { - status.span_content = SpannableHelper.convert(context, status, status.content); - status.span_spoiler_text = SpannableHelper.convert(context, status, status.spoiler_text); - if (status.translationContent != null) { - status.span_translate = SpannableHelper.convert(context, status, status.translationContent); - } - if (status.account == null) { - return status; - } - status.account.span_display_name = SpannableHelper.convertA(context, status.account.display_name, true); - if (status.poll != null) { - for (Poll.PollItem pollItem : status.poll.options) { - pollItem.span_title = SpannableHelper.convert(context, status, pollItem.title, false); - } - } - if (status.reblog != null) { - status.reblog.span_content = SpannableHelper.convert(context, status, status.reblog.content); - if (status.reblog.translationContent != null) { - status.reblog.span_translate = SpannableHelper.convert(context, status, status.reblog.translationContent); - } - status.reblog.span_spoiler_text = SpannableHelper.convert(context, status, status.reblog.spoiler_text); - status.reblog.account.span_display_name = SpannableHelper.convertA(context, status.reblog.account.display_name, true); - if (status.reblog.poll != null) { - for (Poll.PollItem pollItem : status.reblog.poll.options) { - pollItem.span_title = SpannableHelper.convert(context, status, pollItem.title, false); - } - } - } - } - return status; - } - - - public static List convertAccounts(Context context, List accounts) { - if (accounts != null) { - for (Account account : accounts) { - convertAccount(context, account); - } - } - return accounts; - } - - public static Account convertAccount(Context context, Account account) { - if (account != null) { - account.span_display_name = SpannableHelper.convertA(context, account.display_name, true); - account.span_note = SpannableHelper.convertA(context, account.note, false); - if (account.fields != null && account.fields.size() > 0) { - List fields = new ArrayList<>(); - for (Field field : account.fields) { - field.value_span = SpannableHelper.convertA(context, field.value, false); - fields.add(field); - } - account.fields = fields; - } - } - return account; - } - - - /** - * Convert HTML content to text. Also, it handles click on link - * This needs to be run asynchronously - * - * @param context {@link Context} - * @param text String - text to convert, it can be display name or bio - * @return Spannable string - */ - private static Spannable convertA(@NonNull Context context, String text, boolean limitedToDisplayName) { - SpannableString initialContent; - if (text == null) { - return null; - } - if (!limitedToDisplayName) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY)); - else - initialContent = new SpannableString(Html.fromHtml(text)); - } else { - initialContent = new SpannableString(text); - } - SpannableStringBuilder content = new SpannableStringBuilder(initialContent); - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); - for (URLSpan span : urls) - content.removeSpan(span); - //--- URLs ---- - Matcher matcherALink = Patterns.WEB_URL.matcher(content); - - int offSetTruncate = 0; - while (matcherALink.find()) { - int matchStart = matcherALink.start() - offSetTruncate; - int matchEnd = matchStart + matcherALink.group().length(); - //Get real URL - if (matcherALink.start(1) > matcherALink.end(1) || matcherALink.end() > content.length()) { - continue; - } - final String url = content.toString().substring(matchStart, matchEnd); - //Truncate URL if needed - //TODO: add an option to disable truncated URLs - String urlText = url; - if (url.length() > 30 && matchStart < matchEnd) { - urlText = urlText.substring(0, 30); - urlText += "…"; - content.replace(matchStart, matchEnd, urlText); - matchEnd = matcherALink.end() - (url.length() - urlText.length()); - offSetTruncate += (url.length() - urlText.length()); - } - if (!urlText.startsWith("http")) { - continue; - } - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - Helper.openBrowser(context, url); - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - - // --- For all patterns defined in Helper class --- - for (Map.Entry entry : Helper.patternHashMap.entrySet()) { - Helper.PatternType patternType = entry.getKey(); - Pattern pattern = entry.getValue(); - Matcher matcher = pattern.matcher(content); - while (matcher.find()) { - int matchStart = matcher.start(); - int matchEnd = matcher.end(); - if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) { - URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class); - content.removeSpan(span); - String word = content.toString().substring(matchStart, matchEnd); - content.setSpan(new ClickableSpan() { - @Override - public void onClick(@NonNull View textView) { - textView.setTag(CLICKABLE_SPAN); - switch (patternType) { - case TAG: - Intent intent = new Intent(context, HashTagActivity.class); - Bundle b = new Bundle(); - b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - case GROUP: - break; - case MENTION_LONG: - case MENTION: - intent = new Intent(context, ProfileActivity.class); - b = new Bundle(); - b.putString(Helper.ARG_MENTION, word.trim()); - intent.putExtras(b); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - break; - } - } - - @Override - public void updateDrawState(@NonNull TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - ds.setColor(linkColor); - } - }, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - } - } - return trimSpannable(new SpannableStringBuilder(content)); - } - /** * Makes the move to account clickable * @@ -1128,4 +677,9 @@ public class SpannableHelper { } return spannableString; } + + + public interface EmojiCallback { + void transformationDone(String id); + } } diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java index 05e1b2fa9..53b7303d9 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java @@ -37,6 +37,7 @@ import androidx.lifecycle.ViewModelStoreOwner; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.RecyclerView; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; @@ -45,7 +46,6 @@ import app.fedilab.android.R; import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.databinding.DrawerAccountBinding; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.AccountsVM; @@ -225,21 +225,17 @@ public class AccountAdapter extends RecyclerView.Adapter { - if (!account.emojiFetched) { - account.emojiFetched = true; - accountViewHolder.binding.displayName.post(() -> adapter.notifyItemChanged(position)); - } - }); - accountViewHolder.binding.displayName.setText(account.span_display_name, TextView.BufferType.SPANNABLE); + accountViewHolder.binding.displayName.setText( + account.getSpanDisplayName(context, + new WeakReference<>(accountViewHolder.binding.displayName), + id -> adapter.notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); accountViewHolder.binding.username.setText(String.format("@%s", account.acct)); - CustomEmoji.displayEmoji(context, account.emojis, account.span_note, accountViewHolder.binding.bio, account.id, id -> { - if (!account.emojiFetched) { - account.emojiFetched = true; - accountViewHolder.binding.bio.post(() -> adapter.notifyItemChanged(position)); - } - }); - accountViewHolder.binding.bio.setText(account.span_note, TextView.BufferType.SPANNABLE); + accountViewHolder.binding.bio.setText( + account.getSpanNote(context, + new WeakReference<>(accountViewHolder.binding.bio), + id -> adapter.notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); } public int getCount() { diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java index 77accd35b..cbcaef6e4 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java @@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import androidx.recyclerview.widget.RecyclerView; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -78,7 +79,11 @@ public class AccountListAdapter extends RecyclerView.Adapter(holder.binding.displayName), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); holder.binding.username.setText(String.format("@%s", account.acct)); if (searchList != 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 34509b108..d1e667221 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 @@ -73,6 +73,7 @@ import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import java.io.File; +import java.lang.ref.WeakReference; import java.text.Normalizer; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -103,7 +104,6 @@ import app.fedilab.android.databinding.DrawerStatusComposeBinding; import app.fedilab.android.databinding.DrawerStatusSimpleBinding; import app.fedilab.android.databinding.PopupMediaDescriptionBinding; import app.fedilab.android.exception.DBException; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.ThemeHelper; @@ -1041,25 +1041,25 @@ public class ComposeAdapter extends RecyclerView.Adapter { - if (!status.emojiFetched) { - status.emojiFetched = true; - holder.binding.statusContent.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.statusContent.setText(status.span_content, TextView.BufferType.SPANNABLE); + holder.binding.statusContent.setText( + status.getSpanContent(context, + new WeakReference<>(holder.binding.statusContent), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); - CustomEmoji.displayEmoji(context, status.account.emojis, status.account.span_display_name, holder.binding.displayName, status.id, id -> { - if (!status.account.emojiFetched) { - status.account.emojiFetched = true; - holder.binding.statusContent.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.displayName.setText(status.account.span_display_name, TextView.BufferType.SPANNABLE); + holder.binding.displayName.setText( + status.account.getSpanDisplayName(context, + new WeakReference<>(holder.binding.displayName), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); holder.binding.username.setText(String.format("@%s", status.account.acct)); if (status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) { holder.binding.spoiler.setVisibility(View.VISIBLE); - holder.binding.spoiler.setText(status.span_spoiler_text, TextView.BufferType.SPANNABLE); + holder.binding.spoiler.setText( + status.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); holder.binding.spoiler.setText(null); 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 70f2484d7..561e34b2c 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 @@ -36,6 +36,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Timer; @@ -49,7 +50,6 @@ import app.fedilab.android.client.entities.api.Conversation; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.databinding.DrawerConversationBinding; import app.fedilab.android.databinding.ThumbnailBinding; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; @@ -138,26 +138,22 @@ public class ConversationAdapter extends RecyclerView.Adapter { - if (!conversation.last_status.emojiFetched) { - conversation.last_status.emojiFetched = true; - holder.binding.spoiler.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.spoiler.setText(conversation.last_status.span_spoiler_text, TextView.BufferType.SPANNABLE); + holder.binding.spoiler.setText( + conversation.last_status.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> notifyItemChanged(position)), + TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); holder.binding.spoilerExpand.setVisibility(View.GONE); holder.binding.spoiler.setText(null); } //--- MAIN CONTENT --- - CustomEmoji.displayEmoji(context, conversation.last_status.emojis, conversation.last_status.span_content, holder.binding.statusContent, conversation.last_status.id, id -> { - if (!conversation.last_status.emojiFetched) { - conversation.last_status.emojiFetched = true; - holder.binding.statusContent.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.statusContent.setText(conversation.last_status.span_content, TextView.BufferType.SPANNABLE); + holder.binding.statusContent.setText( + conversation.last_status.getSpanContent(context, + new WeakReference<>(holder.binding.statusContent), + id -> notifyItemChanged(position)), + 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 27150f845..d383571e1 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 @@ -21,8 +21,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.text.Spannable; -import android.text.SpannableString; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -34,6 +32,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelStoreOwner; import androidx.recyclerview.widget.RecyclerView; +import java.lang.ref.WeakReference; import java.util.List; import java.util.Locale; @@ -45,7 +44,6 @@ import app.fedilab.android.databinding.DrawerFetchMoreBinding; import app.fedilab.android.databinding.DrawerFollowBinding; import app.fedilab.android.databinding.DrawerStatusNotificationBinding; import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.viewmodel.mastodon.SearchVM; @@ -118,24 +116,37 @@ public class NotificationAdapter extends RecyclerView.Adapter - Not null when calling from notification adapter + * @param id String - Current status + * @return int - position in real time + */ + public static int getPositionAsync(List notificationList, String id) { + int position = 0; + if (notificationList != null) { + for (Notification notification : notificationList) { + if (notification.status != null && notification.status.id.compareTo(id) == 0) { + break; + } + position++; + } + } + return position; + } + @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { Notification notification = notificationList.get(position); if (getItemViewType(position) == TYPE_FOLLOW || getItemViewType(position) == TYPE_FOLLOW_REQUEST) { ViewHolderFollow holderFollow = (ViewHolderFollow) viewHolder; MastodonHelper.loadPPMastodon(holderFollow.binding.avatar, notification.account); - if (notification.account.span_display_name == null && notification.account.display_name != null) { - notification.account.span_display_name = new SpannableString(notification.account.display_name); - } else { - notification.account.span_display_name = new SpannableString(notification.account.username); - } - CustomEmoji.displayEmoji(context, notification.account.emojis, notification.account.span_display_name, holderFollow.binding.displayName, notification.id, id -> { - if (!notification.account.emojiFetched) { - notification.account.emojiFetched = true; - holderFollow.binding.displayName.post(() -> notifyItemChanged(position)); - } - }); - holderFollow.binding.displayName.setText(notification.account.span_display_name, TextView.BufferType.SPANNABLE); + holderFollow.binding.displayName.setText( + notification.account.getSpanDisplayName(context, + new WeakReference<>(holderFollow.binding.displayName), + id -> notifyItemChanged(getPositionAsync(notificationList, id))), + TextView.BufferType.SPANNABLE); holderFollow.binding.username.setText(String.format("@%s", notification.account.acct)); if (getItemViewType(position) == TYPE_FOLLOW_REQUEST) { holderFollow.binding.rejectButton.setVisibility(View.VISIBLE); @@ -197,19 +208,18 @@ public class NotificationAdapter extends RecyclerView.Adapter { - if (!notification.account.emojiFetched) { - notification.account.emojiFetched = true; - holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); - } - }); - holderStatus.bindingNotification.status.displayName.setText(title, TextView.BufferType.SPANNABLE); + notification.account.display_name = title; + holderStatus.bindingNotification.status.displayName.setText( + notification.account.getSpanDisplayName(context, + new WeakReference<>(holderStatus.bindingNotification.status.displayName), + id -> holderStatus.bindingNotification.status.displayName.post(() -> notifyItemChanged(getPositionAsync(notificationList, id)))), + TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.containerTransparent.setAlpha(.1f); if (notification.status != null && notification.status.visibility.equalsIgnoreCase("direct")) { @@ -220,14 +230,14 @@ public class NotificationAdapter extends RecyclerView.Adapter 0) { if (notification.type.equals("favourite")) { @@ -276,12 +286,12 @@ public class NotificationAdapter extends RecyclerView.Adapter { - if (!notification.account.emojiFetched) { - notification.account.emojiFetched = true; - holderStatus.binding.displayName.post(() -> notifyItemChanged(position)); - } - }); + notification.account.display_name = title; + holderStatus.bindingNotification.status.displayName.setText( + notification.account.getSpanDisplayName(context, + new WeakReference<>(holderStatus.bindingNotification.status.displayName), + id -> holderStatus.bindingNotification.status.displayName.post(() -> notifyItemChanged(getPositionAsync(notificationList, id)))), + TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.displayName.setText(title, TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.status.actionButtons.setVisibility(View.GONE); @@ -289,6 +299,7 @@ public class NotificationAdapter extends RecyclerView.Adapter //--- ACCOUNT INFO --- MastodonHelper.loadPPMastodon(holder.binding.avatar, statusToDeal.account); - Spannable span_display_name = statusToDeal.account.span_display_name; - if (span_display_name == null || span_display_name.toString().trim().length() == 0) { - span_display_name = new SpannableString(statusToDeal.account.username); - } - CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, span_display_name, holder.binding.displayName, status.id, id -> { - if (!statusToDeal.account.emojiFetched) { - statusToDeal.account.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.displayName.setText(span_display_name, TextView.BufferType.SPANNABLE); + holder.binding.displayName.setText( + statusToDeal.account.getSpanDisplayName(context, + new WeakReference<>(holder.binding.displayName), + id -> holder.binding.displayName.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); if (theme_text_header_1_line != -1) { holder.binding.displayName.setTextColor(theme_text_header_1_line); } @@ -823,16 +814,11 @@ public class StatusAdapter extends RecyclerView.Adapter if (expand_cw || expand) { holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_spoiler_text, holder.binding.spoiler, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE); + holder.binding.spoiler.setText( + statusToDeal.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> holder.binding.spoiler.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); statusToDeal.isExpended = true; statusToDeal.isMediaDisplayed = true; } else { @@ -843,16 +829,12 @@ public class StatusAdapter extends RecyclerView.Adapter }); holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_spoiler_text, holder.binding.spoiler, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE); + + holder.binding.spoiler.setText( + statusToDeal.getSpanSpoiler(context, + new WeakReference<>(holder.binding.spoiler), + id -> holder.binding.spoiler.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); } if (statusToDeal.isExpended) { holder.binding.spoilerExpand.setText(context.getString(R.string.hide_content)); @@ -868,20 +850,13 @@ public class StatusAdapter extends RecyclerView.Adapter //--- BOOSTER INFO --- if (status.reblog != null) { MastodonHelper.loadPPMastodon(holder.binding.statusBoosterAvatar, status.account); - Spannable span_display_name_boost = status.account.span_display_name; - if (span_display_name_boost == null || span_display_name_boost.toString().trim().length() == 0) { - span_display_name_boost = new SpannableString(status.account.username); - } - CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, span_display_name_boost, holder.binding.statusBoosterDisplayName, status.id, id -> { - if (!statusToDeal.account.emojiFetched) { - statusToDeal.account.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.statusBoosterDisplayName.setText(span_display_name_boost, TextView.BufferType.SPANNABLE); + + holder.binding.statusBoosterDisplayName.setText( + status.account.getSpanDisplayName(context, + new WeakReference<>(holder.binding.statusBoosterDisplayName), + id -> holder.binding.statusBoosterDisplayName.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); + holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE); holder.binding.boosterDivider.setVisibility(View.VISIBLE); if (theme_text_header_1_line != -1) { @@ -913,18 +888,15 @@ public class StatusAdapter extends RecyclerView.Adapter break; } //--- MAIN CONTENT --- - - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_content, holder.binding.statusContent, status.id, id -> { - if (!status.emojiFetched) { - status.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - - holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); + holder.binding.statusContent.setText( + statusToDeal.getSpanContent(context, + new WeakReference<>(holder.binding.statusContent), + id -> holder.binding.statusContent.post(() -> { + Log.v(Helper.TAG, "notifiy: " + id); + Log.v(Helper.TAG, "position: " + getPositionAsync(notificationList, statusList, id)); + adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)); + })), + TextView.BufferType.SPANNABLE); if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); holder.binding.statusContent.setEllipsize(TextUtils.TruncateAt.END); @@ -955,16 +927,11 @@ public class StatusAdapter extends RecyclerView.Adapter } if (statusToDeal.translationContent != null) { holder.binding.containerTrans.setVisibility(View.VISIBLE); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, statusToDeal.span_translate, holder.binding.statusContentTranslated, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - holder.binding.statusContentTranslated.setText(statusToDeal.span_translate, TextView.BufferType.SPANNABLE); + holder.binding.statusContentTranslated.setText( + statusToDeal.getSpanTranslate(context, + new WeakReference<>(holder.binding.statusContentTranslated), + id -> holder.binding.statusContentTranslated.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); } else { holder.binding.containerTrans.setVisibility(View.GONE); } @@ -1226,16 +1193,11 @@ public class StatusAdapter extends RecyclerView.Adapter pollItemBinding.pollItemPercent.setTextColor(theme_text_color); pollItemBinding.pollItemText.setTextColor(theme_text_color); } - CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollItem.span_title, pollItemBinding.pollItemText, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - pollItemBinding.pollItemText.setText(pollItem.span_title, TextView.BufferType.SPANNABLE); + pollItemBinding.pollItemText.setText( + pollItem.getSpanTitle(context, statusToDeal, + new WeakReference<>(pollItemBinding.pollItemText), + id -> pollItemBinding.pollItemText.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); pollItemBinding.pollItemValue.setProgress((int) value); if (pollItem.votes_count == greaterValue) { pollItemBinding.pollItemPercent.setTypeface(null, Typeface.BOLD); @@ -1263,16 +1225,11 @@ public class StatusAdapter extends RecyclerView.Adapter for (Poll.PollItem pollOption : statusToDeal.poll.options) { CheckBox cb = new CheckBox(context); cb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollOption.span_title, cb, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - cb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); + cb.setText( + pollOption.getSpanTitle(context, statusToDeal, + new WeakReference<>(cb), + id -> cb.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); holder.binding.poll.multipleChoice.addView(cb); } holder.binding.poll.multipleChoice.setVisibility(View.VISIBLE); @@ -1283,25 +1240,12 @@ public class StatusAdapter extends RecyclerView.Adapter for (Poll.PollItem pollOption : statusToDeal.poll.options) { RadioButton rb = new RadioButton(context); rb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); - CustomEmoji.displayEmoji(context, statusToDeal.account.emojis, pollOption.span_title, rb, status.id, id -> { - if (!statusToDeal.account.emojiFetched) { - statusToDeal.account.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - CustomEmoji.displayEmoji(context, statusToDeal.emojis, pollOption.span_title, rb, status.id, id -> { - if (!statusToDeal.emojiFetched) { - statusToDeal.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - holder.binding.statusContent.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id))); - } - }); - rb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); + rb.setText( + pollOption.getSpanTitle(context, statusToDeal, + new WeakReference<>(rb), + id -> rb.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + TextView.BufferType.SPANNABLE); + holder.binding.poll.singleChoiceRadioGroup.addView(rb); } holder.binding.poll.singleChoiceRadioGroup.setVisibility(View.VISIBLE); @@ -1626,7 +1570,6 @@ public class StatusAdapter extends RecyclerView.Adapter if (translate.getTranslatedContent() != null) { statusToDeal.translationShown = true; statusToDeal.translationContent = translate.getTranslatedContent(); - SpannableHelper.convertStatus(context.getApplicationContext(), statusToDeal); adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal)); } else { Toasty.error(context, context.getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); @@ -1901,16 +1844,11 @@ public class StatusAdapter extends RecyclerView.Adapter .load(status.art_attachment.preview_url) .apply(new RequestOptions().transform(new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) .into(holder.bindingArt.artMedia); - CustomEmoji.displayEmoji(context, status.emojis, status.account.span_display_name, holder.bindingArt.artAcct, status.id, id -> { - if (!status.emojiFetched) { - status.emojiFetched = true; - if (timelineType == Timeline.TimeLineEnum.UNKNOWN) { - return; - } - notifyItemChanged(getPositionAsync(null, statusList, id)); - } - }); - holder.bindingArt.artAcct.setText(status.account.span_display_name, TextView.BufferType.SPANNABLE); + holder.bindingArt.artAcct.setText( + status.account.getSpanDisplayName(context, + new WeakReference<>(holder.bindingArt.artAcct), + id -> holder.bindingArt.artAcct.post(() -> notifyItemChanged(getPositionAsync(null, statusList, id)))), + TextView.BufferType.SPANNABLE); holder.bindingArt.artUsername.setText(String.format(Locale.getDefault(), "@%s", status.account.acct)); holder.bindingArt.artPp.setOnClickListener(v -> { Intent intent = new Intent(context, ProfileActivity.class); diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java index e4be47a13..1ce3b38d4 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java @@ -43,7 +43,6 @@ import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.helper.DividerDecoration; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.viewmodel.mastodon.StatusesVM; @@ -96,11 +95,10 @@ public class FragmentMastodonContext extends Fragment { } } else if (statusPosted != null && statusAdapter != null) { if (requireActivity() instanceof ContextActivity) { - Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); int i = 0; for (Status status : statuses) { - if (status.id.equals(convertStatus.in_reply_to_id)) { - statuses.add((i + 1), convertStatus); + if (status.id.equals(statusPosted.in_reply_to_id)) { + statuses.add((i + 1), statusPosted); statusAdapter.notifyItemInserted((i + 1)); if (requireActivity() instanceof ContextActivity) { //Redraw decorations diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java index d62864015..23c3bf3cf 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonTimeline.java @@ -56,7 +56,6 @@ import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.viewmodel.mastodon.AccountsVM; @@ -118,8 +117,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter. statusAdapter.notifyItemRemoved(position); } } else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) { - Status convertStatus = SpannableHelper.convertStatus(context, statusPosted); - statuses.add(0, convertStatus); + statuses.add(0, statusPosted); statusAdapter.notifyItemInserted(0); } } diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java index 5b8cf38ec..0c9868ba4 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AccountsVM.java @@ -52,7 +52,6 @@ import app.fedilab.android.client.entities.api.Tag; import app.fedilab.android.client.entities.api.Token; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -306,9 +305,6 @@ public class AccountsVM extends AndroidViewModel { } } Account finalAccount = account; - if (finalAccount != null) { - SpannableHelper.convertAccount(getApplication().getApplicationContext(), finalAccount); - } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> accountMutableLiveData.setValue(finalAccount); mainHandler.post(myRunnable); @@ -342,7 +338,7 @@ public class AccountsVM extends AndroidViewModel { try { Response> accountStatusesResponse = accountStatusesCall.execute(); if (accountStatusesResponse.isSuccessful()) { - statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), accountStatusesResponse.body()); + statusList = accountStatusesResponse.body(); pagination = MastodonHelper.getPagination(accountStatusesResponse.headers()); } @@ -378,7 +374,7 @@ public class AccountsVM extends AndroidViewModel { try { Response> followersResponse = followersCall.execute(); if (followersResponse.isSuccessful()) { - accountList = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), followersResponse.body()); + accountList = followersResponse.body(); pagination = MastodonHelper.getPagination(followersResponse.headers()); } } catch (Exception e) { @@ -414,7 +410,7 @@ public class AccountsVM extends AndroidViewModel { try { Response> followingResponse = followingCall.execute(); if (followingResponse.isSuccessful()) { - accountList = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), followingResponse.body()); + accountList = followingResponse.body(); pagination = MastodonHelper.getPagination(followingResponse.headers()); } } catch (Exception e) { @@ -883,11 +879,6 @@ public class AccountsVM extends AndroidViewModel { } } List finalAccountList = accountList; - if (finalAccountList != null) { - for (Account account : finalAccountList) { - SpannableHelper.convertAccount(getApplication().getApplicationContext(), account); - } - } Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> accountListMutableLiveData.setValue(finalAccountList); mainHandler.post(myRunnable); @@ -912,7 +903,7 @@ public class AccountsVM extends AndroidViewModel { Response> bookmarksResponse = bookmarksCall.execute(); if (bookmarksResponse.isSuccessful()) { statusList = bookmarksResponse.body(); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); + statuses.statuses = statusList; statuses.pagination = MastodonHelper.getPagination(bookmarksResponse.headers()); } } catch (Exception e) { @@ -943,7 +934,7 @@ public class AccountsVM extends AndroidViewModel { Response> favouritesResponse = favouritesCall.execute(); if (favouritesResponse.isSuccessful()) { statusList = favouritesResponse.body(); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); + statuses.statuses = statusList; statuses.pagination = MastodonHelper.getPagination(favouritesResponse.headers()); } } catch (Exception e) { @@ -975,7 +966,7 @@ public class AccountsVM extends AndroidViewModel { Response> mutesResponse = mutesCall.execute(); if (mutesResponse.isSuccessful()) { accountList = mutesResponse.body(); - accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); + accounts.accounts = accountList; accounts.pagination = MastodonHelper.getPagination(mutesResponse.headers()); } } catch (Exception e) { @@ -1007,7 +998,7 @@ public class AccountsVM extends AndroidViewModel { Response> blocksResponse = blocksCall.execute(); if (blocksResponse.isSuccessful()) { accountList = blocksResponse.body(); - accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); + accounts.accounts = accountList; accounts.pagination = MastodonHelper.getPagination(blocksResponse.headers()); } } catch (Exception e) { @@ -1303,7 +1294,7 @@ public class AccountsVM extends AndroidViewModel { Response> followRequestsResponse = followRequestsCall.execute(); if (followRequestsResponse.isSuccessful()) { accountList = followRequestsResponse.body(); - accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); + accounts.accounts = accountList; accounts.pagination = MastodonHelper.getPagination(followRequestsResponse.headers()); } } catch (Exception e) { diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java index afca9d266..743cd32a8 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/AnnouncementsVM.java @@ -32,7 +32,6 @@ import java.util.concurrent.TimeUnit; import app.fedilab.android.client.endpoints.MastodonAnnouncementsService; import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.SpannableHelper; import okhttp3.OkHttpClient; import retrofit2.Call; import retrofit2.Response; @@ -83,7 +82,6 @@ public class AnnouncementsVM extends AndroidViewModel { Response> getAnnouncementsResponse = getAnnouncementsCall.execute(); if (getAnnouncementsResponse.isSuccessful()) { announcementList = getAnnouncementsResponse.body(); - SpannableHelper.convertAnnouncement(getApplication(), announcementList); } } catch (Exception e) { e.printStackTrace(); 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 eccded41a..30faf9f66 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 @@ -35,7 +35,6 @@ import app.fedilab.android.client.entities.api.Notifications; import app.fedilab.android.client.entities.api.PushSubscription; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.TimelineHelper; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -103,15 +102,6 @@ public class NotificationsVM extends AndroidViewModel { if (notificationsResponse.isSuccessful()) { List notFilteredNotifications = notificationsResponse.body(); notifications.notifications = TimelineHelper.filterNotification(getApplication().getApplicationContext(), notFilteredNotifications); - if (notifications.notifications != null) { - for (Notification notification : notifications.notifications) { - if (notification != null) { - if (notification.status != null) { - notification.status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), notification.status); - } - } - } - } notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers()); } } catch (Exception e) { @@ -147,9 +137,6 @@ public class NotificationsVM extends AndroidViewModel { Response notificationResponse = notificationCall.execute(); if (notificationResponse.isSuccessful()) { notification = notificationResponse.body(); - if (notification != null) { - notification.status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), notification.status); - } } } catch (Exception e) { e.printStackTrace(); diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java index b4d5f0e07..bbc928aa9 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/SearchVM.java @@ -36,7 +36,6 @@ import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; -import app.fedilab.android.helper.SpannableHelper; import okhttp3.OkHttpClient; import retrofit2.Call; import retrofit2.Response; @@ -112,13 +111,9 @@ public class SearchVM extends AndroidViewModel { if (results != null) { if (results.statuses == null) { results.statuses = new ArrayList<>(); - } else { - results.statuses = SpannableHelper.convertStatus(getApplication(), results.statuses); } if (results.accounts == null) { results.accounts = new ArrayList<>(); - } else { - results.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), results.accounts); } if (results.hashtags == null) { results.hashtags = new ArrayList<>(); @@ -144,7 +139,6 @@ public class SearchVM extends AndroidViewModel { try { results.statuses = new ArrayList<>(); List statuses = new StatusCache(getApplication()).searchStatus(StatusCache.CacheEnum.HOME, instance, userId, q); - statuses = SpannableHelper.convertStatus(getApplication(), statuses); results.statuses.addAll(statuses); } catch (DBException e) { e.printStackTrace(); diff --git a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java index c97c2daf0..6f4bf1594 100644 --- a/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java +++ b/app/src/main/java/app/fedilab/android/viewmodel/mastodon/StatusesVM.java @@ -47,7 +47,6 @@ import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.TimelineHelper; import okhttp3.Headers; import okhttp3.MultipartBody; @@ -279,7 +278,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } } catch (Exception e) { e.printStackTrace(); @@ -356,13 +355,7 @@ public class StatusesVM extends AndroidViewModel { context = contextResponse.body(); if (context != null) { TimelineHelper.filterStatus(getApplication().getApplicationContext(), context.descendants, TimelineHelper.FilterTimeLineType.CONTEXT); - for (Status status : context.descendants) { - SpannableHelper.convertStatus(getApplication().getApplicationContext(), status); - } TimelineHelper.filterStatus(getApplication().getApplicationContext(), context.ancestors, TimelineHelper.FilterTimeLineType.CONTEXT); - for (Status status : context.ancestors) { - SpannableHelper.convertStatus(getApplication().getApplicationContext(), status); - } } } @@ -406,7 +399,7 @@ public class StatusesVM extends AndroidViewModel { try { Response> accountsResponse = accountsCall.execute(); if (accountsResponse.isSuccessful()) { - accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountsResponse.body()); + accounts = accountsResponse.body(); } headers = accountsResponse.headers(); } catch (Exception e) { @@ -451,7 +444,7 @@ public class StatusesVM extends AndroidViewModel { try { Response> accountsResponse = accountsCall.execute(); if (accountsResponse.isSuccessful()) { - accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountsResponse.body()); + accounts = accountsResponse.body(); } headers = accountsResponse.headers(); } catch (Exception e) { @@ -489,7 +482,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -533,7 +526,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -579,7 +572,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -623,7 +616,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -667,7 +660,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -711,7 +704,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -755,7 +748,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -799,7 +792,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -843,7 +836,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); @@ -887,7 +880,7 @@ public class StatusesVM extends AndroidViewModel { try { Response statusResponse = statusCall.execute(); if (statusResponse.isSuccessful()) { - status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); + status = statusResponse.body(); } else { if (statusResponse.errorBody() != null) { errorMessage = statusResponse.errorBody().string(); 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 47defbdf5..bfd0ace12 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 @@ -48,7 +48,6 @@ import app.fedilab.android.client.entities.peertube.PeertubeVideo; import app.fedilab.android.exception.DBException; import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.MastodonHelper; -import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.helper.TimelineHelper; import okhttp3.OkHttpClient; import retrofit2.Call; @@ -121,7 +120,7 @@ public class TimelinesVM extends AndroidViewModel { Response> publicTlResponse = publicTlCall.execute(); if (publicTlResponse.isSuccessful()) { statusList = publicTlResponse.body(); - statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); + statusList = statusList; } } catch (Exception e) { e.printStackTrace(); @@ -190,8 +189,7 @@ public class TimelinesVM extends AndroidViewModel { Response> publicTlResponse = publicTlCall.execute(); if (publicTlResponse.isSuccessful()) { List notFilteredStatuses = publicTlResponse.body(); - List filteredStatuses = TimelineHelper.filterStatus(getApplication(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = MastodonHelper.getPagination(publicTlResponse.headers()); } } catch (Exception e) { @@ -232,7 +230,7 @@ public class TimelinesVM extends AndroidViewModel { statusList.add(status); } } - statuses.statuses = SpannableHelper.convertNitterStatus(statusList); + statuses.statuses = statusList; String max_id = publicTlResponse.headers().get("min-id"); statuses.pagination = new Pagination(); statuses.pagination.max_id = max_id; @@ -278,8 +276,7 @@ public class TimelinesVM extends AndroidViewModel { statusList.add(status); } } - List filteredStatuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = new Pagination(); if (statusList.size() > 0) { statuses.pagination.min_id = statusList.get(0).id; @@ -325,8 +322,7 @@ public class TimelinesVM extends AndroidViewModel { statusList.add(status); } } - List filteredStatuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = new Pagination(); if (statusList.size() > 0) { //These values are not used. @@ -407,8 +403,7 @@ public class TimelinesVM extends AndroidViewModel { Response> hashTagTlResponse = hashTagTlCall.execute(); if (hashTagTlResponse.isSuccessful()) { List notFilteredStatuses = hashTagTlResponse.body(); - List filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.pagination = MastodonHelper.getPagination(hashTagTlResponse.headers()); } } catch (Exception e) { @@ -450,8 +445,7 @@ public class TimelinesVM extends AndroidViewModel { Response> homeTlResponse = homeTlCall.execute(); if (homeTlResponse.isSuccessful()) { List notFilteredStatuses = homeTlResponse.body(); - List filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME); statuses.pagination = MastodonHelper.getPagination(homeTlResponse.headers()); if (!fetchingMissing) { for (Status status : statuses.statuses) { @@ -503,8 +497,7 @@ public class TimelinesVM extends AndroidViewModel { statuses = statusCacheDAO.geStatuses(StatusCache.CacheEnum.HOME, instance, user_id, maxId, minId, sinceId); if (statuses != null) { - List filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME); - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses); + statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME); if (statuses.statuses != null && statuses.statuses.size() > 0) { statuses.pagination = new Pagination(); statuses.pagination.min_id = statuses.statuses.get(0).id; @@ -571,7 +564,7 @@ public class TimelinesVM extends AndroidViewModel { try { Response> listTlResponse = listTlCall.execute(); if (listTlResponse.isSuccessful()) { - statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), listTlResponse.body()); + statuses.statuses = listTlResponse.body(); statuses.pagination = MastodonHelper.getPagination(listTlResponse.headers()); } } catch (Exception e) { @@ -607,11 +600,6 @@ public class TimelinesVM extends AndroidViewModel { Response> conversationsResponse = conversationsCall.execute(); if (conversationsResponse.isSuccessful()) { conversations.conversations = conversationsResponse.body(); - if (conversations.conversations != null) { - for (Conversation conversation : conversations.conversations) { - conversation.last_status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), conversation.last_status); - } - } conversations.pagination = MastodonHelper.getPagination(conversationsResponse.headers()); } } catch (Exception e) { From 8e0808dcda9090447f90279e6ed03e2a2219ccda Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 14:13:12 +0200 Subject: [PATCH 31/50] some changes --- .../activities/AdminAccountActivity.java | 3 +- .../activities/AdminReportActivity.java | 3 +- .../android/activities/ProfileActivity.java | 6 +-- .../android/client/entities/api/Account.java | 24 ++---------- .../android/client/entities/api/Poll.java | 12 ++---- .../android/client/entities/api/Status.java | 39 ++++--------------- .../android/helper/SpannableHelper.java | 31 +-------------- .../android/ui/drawer/AccountAdapter.java | 6 +-- .../android/ui/drawer/AccountListAdapter.java | 3 +- .../android/ui/drawer/ComposeAdapter.java | 9 ++--- .../ui/drawer/ConversationAdapter.java | 6 +-- .../ui/drawer/NotificationAdapter.java | 9 ++--- .../android/ui/drawer/StatusAdapter.java | 35 +++++------------ 13 files changed, 41 insertions(+), 145 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java index 94e63ecfc..b31c8194e 100644 --- a/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AdminAccountActivity.java @@ -308,8 +308,7 @@ public class AdminAccountActivity extends BaseActivity { binding.accountDn.setText( account.getSpanDisplayName(AdminAccountActivity.this, - new WeakReference<>(binding.accountDn), - id -> binding.accountDn.invalidate()), + new WeakReference<>(binding.accountDn)), TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { diff --git a/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java index 4feea554c..e3c97445e 100644 --- a/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/AdminReportActivity.java @@ -325,8 +325,7 @@ public class AdminReportActivity extends BaseActivity { binding.accountDn.setText( account.getSpanDisplayName(AdminReportActivity.this, - new WeakReference<>(binding.accountDn), - id -> binding.accountDn.invalidate()), + new WeakReference<>(binding.accountDn)), TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setOnLongClickListener(v -> { diff --git a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java index 7371dbbdf..2d8930499 100644 --- a/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ProfileActivity.java @@ -350,8 +350,7 @@ public class ProfileActivity extends BaseActivity { binding.accountDn.setText( account.getSpanDisplayName(ProfileActivity.this, - new WeakReference<>(binding.accountDn), - id -> binding.accountDn.invalidate()), + new WeakReference<>(binding.accountDn)), TextView.BufferType.SPANNABLE); binding.accountUn.setText(String.format("@%s", account.acct)); @@ -368,8 +367,7 @@ public class ProfileActivity extends BaseActivity { }); binding.accountNote.setText( account.getSpanNote(ProfileActivity.this, - new WeakReference<>(binding.accountNote), - id -> binding.accountNote.invalidate()), + new WeakReference<>(binding.accountNote)), TextView.BufferType.SPANNABLE); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java index 5450e7a26..94623d4b0 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java @@ -79,32 +79,16 @@ public class Account implements Serializable { @SerializedName("moved") public Account moved; - public transient boolean emojiDisplayNameFetched = false; - public transient boolean emojiNoteFetched = false; - //Some extra spannable element - They will be filled automatically when fetching the account - private transient Spannable span_display_name; - private transient Spannable span_note; - - public synchronized Spannable getSpanDisplayName(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { - if (span_display_name != null) { - return span_display_name; - } + public synchronized Spannable getSpanDisplayName(Context context, WeakReference viewWeakReference) { if (display_name == null) { display_name = username; } - span_display_name = SpannableHelper.convert(context, display_name, null, this, true, viewWeakReference, !emojiDisplayNameFetched ? callback : null); - emojiDisplayNameFetched = true; - return span_display_name; + return SpannableHelper.convert(context, display_name, null, this, true, viewWeakReference); } - public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { - if (span_note != null) { - return span_note; - } - span_note = SpannableHelper.convert(context, note, null, this, true, viewWeakReference, !emojiNoteFetched ? callback : null); - emojiNoteFetched = true; - return span_note; + public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference) { + return SpannableHelper.convert(context, note, null, this, true, viewWeakReference); } public transient RelationShip relationShip; diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java index 7df3e7c03..4cd8cff9f 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java @@ -58,16 +58,10 @@ public class Poll implements Serializable { @SerializedName("votes_count") public int votes_count; - //Some extra spannable element - They will be filled automatically when fetching the poll - public transient Spannable span_title; - public transient boolean emojiTitleFetched = false; + public Spannable span_title; - public Spannable getSpanTitle(Context context, Status status, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { - if (span_title != null) { - return span_title; - } - span_title = SpannableHelper.convert(context, title, status, null, true, viewWeakReference, !emojiTitleFetched ? callback : null); - emojiTitleFetched = true; + public Spannable getSpanTitle(Context context, Status status, WeakReference viewWeakReference) { + span_title = SpannableHelper.convert(context, title, status, null, true, viewWeakReference); return span_title; } } 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 159feea2d..a1e3e70d9 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 @@ -93,9 +93,6 @@ public class Status implements Serializable, Cloneable { public Attachment art_attachment; - public transient boolean emojiContentFetched = false; - public transient boolean emojiSpoilerFetched = false; - public transient boolean emojiTranslateFetched = false; public boolean isExpended = false; public boolean isTruncated = true; public boolean isFetchMore = false; @@ -110,44 +107,22 @@ public class Status implements Serializable, Cloneable { public transient int cursorPosition = 0; public transient boolean submitted = false; //Some extra spannable element - They will be filled automatically when fetching the status - private transient Spannable span_content; - private transient Spannable span_spoiler_text; - private transient Spannable span_translate; - public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { - if (span_content != null) { - return span_content; - } - span_content = SpannableHelper.convert(context, content, this, null, true, viewWeakReference, !emojiContentFetched ? callback : null); - emojiContentFetched = true; - return span_content; + public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference) { + return SpannableHelper.convert(context, content, this, null, true, viewWeakReference); } public Spannable getSpanContentNitter() { - if (span_content != null) { - return span_content; - } - span_content = SpannableHelper.convertNitter(content); - return span_content; + return SpannableHelper.convertNitter(content); } - public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { - if (span_spoiler_text != null) { - return span_spoiler_text; - } - span_spoiler_text = SpannableHelper.convert(context, spoiler_text, this, null, true, viewWeakReference, !emojiSpoilerFetched ? callback : null); - emojiSpoilerFetched = true; - return span_spoiler_text; + public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference) { + return SpannableHelper.convert(context, spoiler_text, this, null, true, viewWeakReference); } - public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference, SpannableHelper.EmojiCallback callback) { - if (span_translate != null) { - return span_translate; - } - span_translate = SpannableHelper.convert(context, translationContent, this, null, true, viewWeakReference, !emojiTranslateFetched ? callback : null); - emojiTranslateFetched = true; - return span_translate; + public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference) { + return SpannableHelper.convert(context, translationContent, this, null, true, viewWeakReference); } @NonNull 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 39fec6469..8c9a5bbe3 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -40,7 +40,6 @@ import android.text.TextPaint; import android.text.style.ClickableSpan; import android.text.style.ImageSpan; import android.text.style.URLSpan; -import android.util.Log; import android.util.Patterns; import android.view.LayoutInflater; import android.view.View; @@ -88,8 +87,7 @@ public class SpannableHelper { public static Spannable convert(Context context, String text, Status status, Account account, boolean convertHtml, - WeakReference viewWeakReference, - EmojiCallback listener) { + WeakReference viewWeakReference) { SpannableString initialContent; if (text == null) { @@ -97,16 +95,13 @@ public class SpannableHelper { } SpannableStringBuilder content; View view = viewWeakReference.get(); - String id = null; List mentionList = null; List emojiList = null; if (status != null) { mentionList = status.mentions; emojiList = status.emojis; - id = status.id; } else if (account != null) { emojiList = account.emojis; - id = account.id; } HashMap urlDetails = new HashMap<>(); if (convertHtml) { @@ -142,24 +137,12 @@ public class SpannableHelper { if (emojiList != null && emojiList.size() > 0) { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); - int count = 1; for (Emoji emoji : emojiList) { - int finalCount = count; - List finalEmojiList = emojiList; - String finalId = id; - List finalEmojiList1 = emojiList; Glide.with(context) .asDrawable() .load(animate ? emoji.url : emoji.static_url) .into( new CustomTarget() { - @Override - public void onLoadFailed(@Nullable Drawable errorDrawable) { - super.onLoadFailed(errorDrawable); - if (finalCount == finalEmojiList.size() && listener != null) { - listener.transformationDone(finalId); - } - } @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { @@ -202,11 +185,7 @@ public class SpannableHelper { ((Animatable) resource).start(); } - Log.v(Helper.TAG, ">: " + emoji.shortcode + " --> " + listener + " - " + finalCount + " <> " + finalEmojiList1.size()); - if (finalCount == finalEmojiList.size() && listener != null) { - Log.v(Helper.TAG, "OK FOR: " + emoji.shortcode + " --> " + finalId); - listener.transformationDone(finalId); - } + } @Override @@ -215,7 +194,6 @@ public class SpannableHelper { } } ); - count++; } } return trimSpannable(new SpannableStringBuilder(content)); @@ -677,9 +655,4 @@ public class SpannableHelper { } return spannableString; } - - - public interface EmojiCallback { - void transformationDone(String id); - } } diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java index 53b7303d9..872980879 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AccountAdapter.java @@ -227,14 +227,12 @@ public class AccountAdapter extends RecyclerView.Adapter(accountViewHolder.binding.displayName), - id -> adapter.notifyItemChanged(position)), + new WeakReference<>(accountViewHolder.binding.displayName)), TextView.BufferType.SPANNABLE); accountViewHolder.binding.username.setText(String.format("@%s", account.acct)); accountViewHolder.binding.bio.setText( account.getSpanNote(context, - new WeakReference<>(accountViewHolder.binding.bio), - id -> adapter.notifyItemChanged(position)), + new WeakReference<>(accountViewHolder.binding.bio)), TextView.BufferType.SPANNABLE); } diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java index cbcaef6e4..356d880cb 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AccountListAdapter.java @@ -81,8 +81,7 @@ public class AccountListAdapter extends RecyclerView.Adapter(holder.binding.displayName), - id -> notifyItemChanged(position)), + new WeakReference<>(holder.binding.displayName)), TextView.BufferType.SPANNABLE); holder.binding.username.setText(String.format("@%s", account.acct)); 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 d1e667221..f6f41080d 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 @@ -1043,22 +1043,19 @@ public class ComposeAdapter extends RecyclerView.Adapter(holder.binding.statusContent), - id -> notifyItemChanged(position)), + new WeakReference<>(holder.binding.statusContent)), TextView.BufferType.SPANNABLE); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); holder.binding.displayName.setText( status.account.getSpanDisplayName(context, - new WeakReference<>(holder.binding.displayName), - id -> notifyItemChanged(position)), + new WeakReference<>(holder.binding.displayName)), TextView.BufferType.SPANNABLE); holder.binding.username.setText(String.format("@%s", status.account.acct)); 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), - id -> notifyItemChanged(position)), + new WeakReference<>(holder.binding.spoiler)), 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 561e34b2c..94e1d748e 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 @@ -140,8 +140,7 @@ public class ConversationAdapter extends RecyclerView.Adapter(holder.binding.spoiler), - id -> notifyItemChanged(position)), + new WeakReference<>(holder.binding.spoiler)), TextView.BufferType.SPANNABLE); } else { holder.binding.spoiler.setVisibility(View.GONE); @@ -151,8 +150,7 @@ public class ConversationAdapter extends RecyclerView.Adapter(holder.binding.statusContent), - id -> notifyItemChanged(position)), + new WeakReference<>(holder.binding.statusContent)), 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 d383571e1..194e94b4f 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 @@ -144,8 +144,7 @@ public class NotificationAdapter extends RecyclerView.Adapter(holderFollow.binding.displayName), - id -> notifyItemChanged(getPositionAsync(notificationList, id))), + new WeakReference<>(holderFollow.binding.displayName)), TextView.BufferType.SPANNABLE); holderFollow.binding.username.setText(String.format("@%s", notification.account.acct)); if (getItemViewType(position) == TYPE_FOLLOW_REQUEST) { @@ -217,8 +216,7 @@ public class NotificationAdapter extends RecyclerView.Adapter(holderStatus.bindingNotification.status.displayName), - id -> holderStatus.bindingNotification.status.displayName.post(() -> notifyItemChanged(getPositionAsync(notificationList, id)))), + new WeakReference<>(holderStatus.bindingNotification.status.displayName)), TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.containerTransparent.setAlpha(.1f); @@ -289,8 +287,7 @@ public class NotificationAdapter extends RecyclerView.Adapter(holderStatus.bindingNotification.status.displayName), - id -> holderStatus.bindingNotification.status.displayName.post(() -> notifyItemChanged(getPositionAsync(notificationList, id)))), + new WeakReference<>(holderStatus.bindingNotification.status.displayName)), TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.displayName.setText(title, TextView.BufferType.SPANNABLE); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); 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 bd373a09d..e6963e266 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 @@ -40,7 +40,6 @@ import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; import android.text.style.ForegroundColorSpan; -import android.util.Log; import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -747,8 +746,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.displayName.setText( statusToDeal.account.getSpanDisplayName(context, - new WeakReference<>(holder.binding.displayName), - id -> holder.binding.displayName.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(holder.binding.displayName)), TextView.BufferType.SPANNABLE); if (theme_text_header_1_line != -1) { holder.binding.displayName.setTextColor(theme_text_header_1_line); @@ -816,8 +814,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), - id -> holder.binding.spoiler.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(holder.binding.spoiler)), TextView.BufferType.SPANNABLE); statusToDeal.isExpended = true; statusToDeal.isMediaDisplayed = true; @@ -832,8 +829,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.spoiler.setText( statusToDeal.getSpanSpoiler(context, - new WeakReference<>(holder.binding.spoiler), - id -> holder.binding.spoiler.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(holder.binding.spoiler)), TextView.BufferType.SPANNABLE); } if (statusToDeal.isExpended) { @@ -853,8 +849,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.statusBoosterDisplayName.setText( status.account.getSpanDisplayName(context, - new WeakReference<>(holder.binding.statusBoosterDisplayName), - id -> holder.binding.statusBoosterDisplayName.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(holder.binding.statusBoosterDisplayName)), TextView.BufferType.SPANNABLE); holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE); @@ -890,12 +885,7 @@ public class StatusAdapter extends RecyclerView.Adapter //--- MAIN CONTENT --- holder.binding.statusContent.setText( statusToDeal.getSpanContent(context, - new WeakReference<>(holder.binding.statusContent), - id -> holder.binding.statusContent.post(() -> { - Log.v(Helper.TAG, "notifiy: " + id); - Log.v(Helper.TAG, "position: " + getPositionAsync(notificationList, statusList, id)); - adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)); - })), + new WeakReference<>(holder.binding.statusContent)), TextView.BufferType.SPANNABLE); if (truncate_toots_size > 0) { holder.binding.statusContent.setMaxLines(truncate_toots_size); @@ -929,8 +919,7 @@ public class StatusAdapter extends RecyclerView.Adapter holder.binding.containerTrans.setVisibility(View.VISIBLE); holder.binding.statusContentTranslated.setText( statusToDeal.getSpanTranslate(context, - new WeakReference<>(holder.binding.statusContentTranslated), - id -> holder.binding.statusContentTranslated.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(holder.binding.statusContentTranslated)), TextView.BufferType.SPANNABLE); } else { holder.binding.containerTrans.setVisibility(View.GONE); @@ -1195,8 +1184,7 @@ public class StatusAdapter extends RecyclerView.Adapter } pollItemBinding.pollItemText.setText( pollItem.getSpanTitle(context, statusToDeal, - new WeakReference<>(pollItemBinding.pollItemText), - id -> pollItemBinding.pollItemText.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(pollItemBinding.pollItemText)), TextView.BufferType.SPANNABLE); pollItemBinding.pollItemValue.setProgress((int) value); if (pollItem.votes_count == greaterValue) { @@ -1227,8 +1215,7 @@ public class StatusAdapter extends RecyclerView.Adapter cb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); cb.setText( pollOption.getSpanTitle(context, statusToDeal, - new WeakReference<>(cb), - id -> cb.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(cb)), TextView.BufferType.SPANNABLE); holder.binding.poll.multipleChoice.addView(cb); } @@ -1242,8 +1229,7 @@ public class StatusAdapter extends RecyclerView.Adapter rb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); rb.setText( pollOption.getSpanTitle(context, statusToDeal, - new WeakReference<>(rb), - id -> rb.post(() -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, id)))), + new WeakReference<>(rb)), TextView.BufferType.SPANNABLE); holder.binding.poll.singleChoiceRadioGroup.addView(rb); @@ -1846,8 +1832,7 @@ public class StatusAdapter extends RecyclerView.Adapter .into(holder.bindingArt.artMedia); holder.bindingArt.artAcct.setText( status.account.getSpanDisplayName(context, - new WeakReference<>(holder.bindingArt.artAcct), - id -> holder.bindingArt.artAcct.post(() -> notifyItemChanged(getPositionAsync(null, statusList, id)))), + new WeakReference<>(holder.bindingArt.artAcct)), TextView.BufferType.SPANNABLE); holder.bindingArt.artUsername.setText(String.format(Locale.getDefault(), "@%s", status.account.acct)); holder.bindingArt.artPp.setOnClickListener(v -> { From 65002bf2351ff5ec50c987369f9d8a0b764e9b33 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 14:39:47 +0200 Subject: [PATCH 32/50] some changes --- .../android/client/entities/api/Account.java | 4 +- .../client/entities/api/Announcement.java | 13 +- .../android/client/entities/api/Field.java | 23 ++- .../android/client/entities/api/Poll.java | 2 +- .../android/client/entities/api/Status.java | 6 +- .../fedilab/android/helper/CustomEmoji.java | 182 ++++++++---------- .../android/helper/SpannableHelper.java | 75 ++------ .../ui/drawer/AnnouncementAdapter.java | 13 +- .../android/ui/drawer/FieldAdapter.java | 11 +- 9 files changed, 146 insertions(+), 183 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java index 94623d4b0..8e2457464 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Account.java @@ -83,12 +83,12 @@ public class Account implements Serializable { if (display_name == null) { display_name = username; } - return SpannableHelper.convert(context, display_name, null, this, true, viewWeakReference); + return SpannableHelper.convert(context, display_name, null, this, null, true, viewWeakReference); } public synchronized Spannable getSpanNote(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, note, null, this, true, viewWeakReference); + return SpannableHelper.convert(context, note, null, this, null, true, viewWeakReference); } public transient RelationShip relationShip; diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java index a642040c6..b015313fd 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Announcement.java @@ -14,13 +14,18 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.view.View; import com.google.gson.annotations.SerializedName; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; +import app.fedilab.android.helper.SpannableHelper; + public class Announcement { @SerializedName("id") public String id; @@ -49,7 +54,9 @@ public class Announcement { @SerializedName("reactions") public List reactions; - //Some extra spannable element - They will be filled automatically when fetching the status - public transient Spannable span_content; - public transient boolean emojiFetched = false; + + public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference) { + return SpannableHelper.convert(context, content, null, null, this, true, viewWeakReference); + } + } diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Field.java b/app/src/main/java/app/fedilab/android/client/entities/api/Field.java index 55754dc6c..da8571564 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Field.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Field.java @@ -14,13 +14,22 @@ package app.fedilab.android.client.entities.api; * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ +import android.content.Context; import android.text.Spannable; +import android.text.style.ForegroundColorSpan; +import android.view.View; + +import androidx.core.content.ContextCompat; import com.google.gson.annotations.SerializedName; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Date; +import app.fedilab.android.R; +import app.fedilab.android.helper.SpannableHelper; + public class Field implements Serializable { @SerializedName("name") public String name; @@ -30,7 +39,19 @@ public class Field implements Serializable { public Date verified_at; //Some extra spannable element - They will be filled automatically when fetching the account - public transient Spannable value_span; + private ForegroundColorSpan value_span; + + public synchronized Spannable getValueSpan(Context context, Account account, WeakReference viewWeakReference) { + + if (verified_at != null && value != null) { + value_span = new ForegroundColorSpan(ContextCompat.getColor(context, R.color.verified_text)); + } + Spannable spannable = SpannableHelper.convert(context, value, null, account, null, true, viewWeakReference); + if (value_span != null && spannable != null) { + spannable.setSpan(value_span, 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return spannable; + } public static class FieldParams implements Serializable { @SerializedName("name") diff --git a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java index 4cd8cff9f..a4e8b5c68 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java +++ b/app/src/main/java/app/fedilab/android/client/entities/api/Poll.java @@ -61,7 +61,7 @@ public class Poll implements Serializable { public Spannable span_title; public Spannable getSpanTitle(Context context, Status status, WeakReference viewWeakReference) { - span_title = SpannableHelper.convert(context, title, status, null, true, viewWeakReference); + span_title = SpannableHelper.convert(context, title, status, null, null, true, viewWeakReference); return span_title; } } 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 a1e3e70d9..b75125e18 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 @@ -109,7 +109,7 @@ public class Status implements Serializable, Cloneable { //Some extra spannable element - They will be filled automatically when fetching the status public synchronized Spannable getSpanContent(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, content, this, null, true, viewWeakReference); + return SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference); } @@ -118,11 +118,11 @@ public class Status implements Serializable, Cloneable { } public synchronized Spannable getSpanSpoiler(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, spoiler_text, this, null, true, viewWeakReference); + return SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference); } public synchronized Spannable getSpanTranslate(Context context, WeakReference viewWeakReference) { - return SpannableHelper.convert(context, translationContent, this, null, true, viewWeakReference); + return SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference); } @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 6f3745250..8b860a691 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -1,125 +1,111 @@ package app.fedilab.android.helper; -/* Copyright 2022 Thomas Schneider - * - * This file is a part of Fedilab - * - * This program is free software; you can redistribute it and/or modify it under the terms of the - * GNU General Public License as published by the Free Software Foundation; either version 3 of the - * License, or (at your option) any later version. - * - * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even - * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. - * - * You should have received a copy of the GNU General Public License along with Fedilab; if not, - * see . */ import android.content.Context; import android.content.SharedPreferences; +import android.graphics.Canvas; +import android.graphics.Paint; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; -import android.text.Spannable; -import android.text.style.ImageSpan; +import android.text.style.ReplacementSpan; +import android.util.Log; import android.view.View; 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.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.lang.ref.WeakReference; import app.fedilab.android.R; -import app.fedilab.android.client.entities.api.Emoji; - -public class CustomEmoji { - public static void displayEmoji(Context context, List emojis, Spannable content, View view, String id, Callback listener) { - SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(view.getContext()); - boolean animate = !sharedpreferences.getBoolean(view.getContext().getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); - int count = 1; - for (Emoji emoji : emojis) { - int finalCount = count; - Glide.with(view.getContext()) - .asDrawable() - .load(animate ? emoji.url : emoji.static_url) - .into( - new CustomTarget() { - @Override - public void onLoadFailed(@Nullable Drawable errorDrawable) { - super.onLoadFailed(errorDrawable); - if (finalCount == emojis.size() && listener != null) { - listener.allEmojisfound(id); - } - } +public class CustomEmoji extends ReplacementSpan { + private final View view; + private final float scale; + private final WeakReference viewWeakReference; + private Drawable imageDrawable; - @Override - public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { - if (content == null) { - return; - } - Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) - .matcher(content); - while (matcher.find()) { - ImageSpan imageSpan; - resource.setBounds(0, 0, (int) Helper.convertDpToPixel(20, context), (int) Helper.convertDpToPixel(20, context)); - resource.setVisible(true, true); - imageSpan = new ImageSpan(resource); - content.setSpan( - imageSpan, matcher.start(), - matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - if (animate && resource instanceof Animatable) { - Drawable.Callback callback = resource.getCallback(); - resource.setCallback(new Drawable.Callback() { - @Override - public void invalidateDrawable(@NonNull Drawable drawable) { - if (callback != null) { - callback.invalidateDrawable(drawable); - } - view.invalidate(); - } - @Override - public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { - if (callback != null) { - callback.scheduleDrawable(drawable, runnable, l); - } - } + CustomEmoji(WeakReference viewWeakReference) { + Context mContext = viewWeakReference.get().getContext(); + this.viewWeakReference = viewWeakReference; + view = viewWeakReference.get(); + SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); + scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.0f); + } - @Override - public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { - if (callback != null) { - callback.unscheduleDrawable(drawable, runnable); - } - } - }); - ((Animatable) resource).start(); + @Override + public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i1, @Nullable Paint.FontMetricsInt fontMetricsInt) { + if (fontMetricsInt != null) { + Paint.FontMetrics fontMetrics = paint.getFontMetrics(); + fontMetricsInt.top = (int) fontMetrics.top; + fontMetricsInt.ascent = (int) fontMetrics.ascent; + fontMetricsInt.descent = (int) fontMetrics.descent; + fontMetricsInt.bottom = (int) fontMetrics.bottom; + } + return (int) (paint.getTextSize() * scale); + } - } - if (finalCount == emojis.size() && listener != null) { - listener.allEmojisfound(id); - } - } - - @Override - public void onLoadCleared(@Nullable Drawable placeholder) { - - } - } - ); - count++; + @Override + public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + if (imageDrawable != null) { + canvas.save(); + int emojiSize = (int) (paint.getTextSize() * scale); + Drawable drawable = imageDrawable; + drawable.setBounds(0, 0, emojiSize, emojiSize); + int transY = bottom - drawable.getBounds().bottom; + transY -= paint.getFontMetrics().descent / 2; + canvas.translate(x, (float) transY); + drawable.draw(canvas); + canvas.restore(); } } - public interface Callback { - void allEmojisfound(String id); - } + public Target getTarget(boolean animate) { + return new CustomTarget() { + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + Log.v(Helper.TAG, "resource: " + resource); + Log.v(Helper.TAG, "instanceof: " + (resource instanceof Animatable)); + View view = viewWeakReference.get(); + if (animate && resource instanceof Animatable) { + Drawable.Callback callback = resource.getCallback(); + resource.setCallback(new Drawable.Callback() { + @Override + public void invalidateDrawable(@NonNull Drawable drawable) { + if (callback != null) { + callback.invalidateDrawable(drawable); + } + view.invalidate(); + } -} + @Override + public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { + if (callback != null) { + callback.scheduleDrawable(drawable, runnable, l); + } + } + + @Override + public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { + if (callback != null) { + callback.unscheduleDrawable(drawable, runnable); + } + } + }); + ((Animatable) resource).start(); + } + imageDrawable = resource; + view.invalidate(); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + } + }; + } +} \ No newline at end of file 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 8c9a5bbe3..56ab2f4e6 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -25,8 +25,6 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.drawable.Animatable; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -38,7 +36,6 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.style.ClickableSpan; -import android.text.style.ImageSpan; import android.text.style.URLSpan; import android.util.Patterns; import android.view.LayoutInflater; @@ -46,13 +43,10 @@ import android.view.View; import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.preference.PreferenceManager; import com.bumptech.glide.Glide; -import com.bumptech.glide.request.target.CustomTarget; -import com.bumptech.glide.request.transition.Transition; import java.io.IOException; import java.lang.ref.WeakReference; @@ -73,6 +67,7 @@ import app.fedilab.android.activities.ContextActivity; import app.fedilab.android.activities.HashTagActivity; import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.client.entities.api.Account; +import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.client.entities.api.Emoji; import app.fedilab.android.client.entities.api.Mention; @@ -85,7 +80,7 @@ public class SpannableHelper { public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN"; public static Spannable convert(Context context, String text, - Status status, Account account, + Status status, Account account, Announcement announcement, boolean convertHtml, WeakReference viewWeakReference) { @@ -138,62 +133,16 @@ public class SpannableHelper { SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); for (Emoji emoji : emojiList) { - Glide.with(context) - .asDrawable() - .load(animate ? emoji.url : emoji.static_url) - .into( - new CustomTarget() { - - @Override - public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { - Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL) - .matcher(content); - while (matcher.find()) { - ImageSpan imageSpan; - resource.setBounds(0, 0, (int) Helper.convertDpToPixel(20, context), (int) Helper.convertDpToPixel(20, context)); - resource.setVisible(true, true); - imageSpan = new ImageSpan(resource); - content.setSpan( - imageSpan, matcher.start(), - matcher.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - } - if (animate && resource instanceof Animatable) { - Drawable.Callback callback = resource.getCallback(); - resource.setCallback(new Drawable.Callback() { - @Override - public void invalidateDrawable(@NonNull Drawable drawable) { - if (callback != null) { - callback.invalidateDrawable(drawable); - } - view.invalidate(); - } - - @Override - public void scheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable, long l) { - if (callback != null) { - callback.scheduleDrawable(drawable, runnable, l); - } - } - - @Override - public void unscheduleDrawable(@NonNull Drawable drawable, @NonNull Runnable runnable) { - if (callback != null) { - callback.unscheduleDrawable(drawable, runnable); - } - } - }); - ((Animatable) resource).start(); - - } - - } - - @Override - public void onLoadCleared(@Nullable Drawable placeholder) { - - } - } - ); + 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)); + } } } return trimSpannable(new SpannableStringBuilder(content)); diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java index 8ae35dafd..6a6d9fad9 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/AnnouncementAdapter.java @@ -37,6 +37,7 @@ import com.vanniktech.emoji.EmojiManager; import com.vanniktech.emoji.EmojiPopup; import com.vanniktech.emoji.one.EmojiOneProvider; +import java.lang.ref.WeakReference; import java.util.List; import app.fedilab.android.BaseMainActivity; @@ -44,7 +45,6 @@ import app.fedilab.android.R; import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Reaction; import app.fedilab.android.databinding.DrawerAnnouncementBinding; -import app.fedilab.android.helper.CustomEmoji; import app.fedilab.android.helper.Helper; import app.fedilab.android.viewmodel.mastodon.AnnouncementsVM; @@ -88,13 +88,10 @@ public class AnnouncementAdapter extends RecyclerView.Adapter { - if (!announcement.emojiFetched) { - announcement.emojiFetched = true; - holder.binding.content.post(() -> notifyItemChanged(position)); - } - }); - holder.binding.content.setText(announcement.span_content, TextView.BufferType.SPANNABLE); + holder.binding.content.setText( + announcement.getSpanContent(context, + new WeakReference<>(holder.binding.content)), + TextView.BufferType.SPANNABLE); if (announcement.starts_at != null) { String dateIni; String dateEnd; diff --git a/app/src/main/java/app/fedilab/android/ui/drawer/FieldAdapter.java b/app/src/main/java/app/fedilab/android/ui/drawer/FieldAdapter.java index df8bbe270..b4b39fc10 100644 --- a/app/src/main/java/app/fedilab/android/ui/drawer/FieldAdapter.java +++ b/app/src/main/java/app/fedilab/android/ui/drawer/FieldAdapter.java @@ -16,9 +16,7 @@ package app.fedilab.android.ui.drawer; import android.content.Context; -import android.text.Spannable; import android.text.method.LinkMovementMethod; -import android.text.style.ForegroundColorSpan; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.TextView; @@ -27,9 +25,11 @@ import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; +import java.lang.ref.WeakReference; import java.util.List; import app.fedilab.android.R; +import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.client.entities.api.Field; import app.fedilab.android.databinding.DrawerFieldBinding; @@ -38,6 +38,7 @@ public class FieldAdapter extends RecyclerView.Adapter fields; private Context context; + private Account account; public FieldAdapter(List fields) { this.fields = fields; @@ -66,9 +67,11 @@ public class FieldAdapter extends RecyclerView.Adapter(holder.binding.value)), + TextView.BufferType.SPANNABLE); holder.binding.value.setMovementMethod(LinkMovementMethod.getInstance()); holder.binding.label.setText(field.name); } From 17f727fd9f058fbf4be20836f80f27d256451d04 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 15:28:23 +0200 Subject: [PATCH 33/50] Fix #258 - Wrong selection after a second search. --- .../fedilab/android/activities/SearchResultTabActivity.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 7fee806a8..1cb92672d 100644 --- a/app/src/main/java/app/fedilab/android/activities/SearchResultTabActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/SearchResultTabActivity.java @@ -53,6 +53,7 @@ public class SearchResultTabActivity extends BaseActivity { private String search; private ActivitySearchResultTabsBinding binding; + private TabLayout.Tab initial; @Override protected void onCreate(Bundle savedInstanceState) { @@ -77,7 +78,8 @@ public class SearchResultTabActivity extends BaseActivity { getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary))); } setTitle(search); - binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags))); + initial = binding.searchTabLayout.newTab(); + binding.searchTabLayout.addTab(initial.setText(getString(R.string.tags))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.accounts))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.action_cache))); @@ -125,7 +127,6 @@ public class SearchResultTabActivity extends BaseActivity { @Override public boolean onQueryTextSubmit(String query) { InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - assert imm != null; imm.hideSoftInputFromWindow(binding.searchTabLayout.getWindowToken(), 0); query = query.replaceAll("^#+", ""); search = query.trim(); @@ -134,6 +135,7 @@ public class SearchResultTabActivity extends BaseActivity { searchView.clearFocus(); setTitle(search); searchView.setIconified(true); + binding.searchTabLayout.selectTab(initial); return false; } From ef58c7bc40fdea03ece54b38527ccb318d5777d9 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 17:46:29 +0200 Subject: [PATCH 34/50] Fix #256 - Can share with one account --- app/src/main/java/app/fedilab/android/BaseMainActivity.java | 2 +- .../java/app/fedilab/android/client/entities/app/Account.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/app/fedilab/android/BaseMainActivity.java b/app/src/main/java/app/fedilab/android/BaseMainActivity.java index 49c3f744d..231becca3 100644 --- a/app/src/main/java/app/fedilab/android/BaseMainActivity.java +++ b/app/src/main/java/app/fedilab/android/BaseMainActivity.java @@ -537,7 +537,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24); new Thread(() -> { try { - List accounts = new Account(BaseMainActivity.this).getAll(); + List accounts = new Account(BaseMainActivity.this).getCrossAccounts(); Handler mainHandler = new Handler(Looper.getMainLooper()); Runnable myRunnable = () -> { binding.navView.getMenu().clear(); diff --git a/app/src/main/java/app/fedilab/android/client/entities/app/Account.java b/app/src/main/java/app/fedilab/android/client/entities/app/Account.java index 1be6458b4..046bc5c72 100644 --- a/app/src/main/java/app/fedilab/android/client/entities/app/Account.java +++ b/app/src/main/java/app/fedilab/android/client/entities/app/Account.java @@ -295,7 +295,7 @@ public class Account extends BaseAccount implements Serializable { } try { Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, null, null, null, null, null, null); - return cursorToListUser(c); + return cursorToListUserWithOwner(c); } catch (Exception e) { return null; } From 6c3356d2bec53979ec7f9391de73621bad7d0480 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 17:46:55 +0200 Subject: [PATCH 35/50] Some fixes --- .../fedilab/android/helper/CustomEmoji.java | 5 ---- .../app/fedilab/android/helper/Helper.java | 9 ++++++++ .../android/helper/SpannableHelper.java | 23 ++++++++++++++++--- 3 files changed, 29 insertions(+), 8 deletions(-) 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 8b860a691..d32304750 100644 --- a/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java +++ b/app/src/main/java/app/fedilab/android/helper/CustomEmoji.java @@ -7,7 +7,6 @@ import android.graphics.Paint; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.text.style.ReplacementSpan; -import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -24,7 +23,6 @@ import app.fedilab.android.R; public class CustomEmoji extends ReplacementSpan { - private final View view; private final float scale; private final WeakReference viewWeakReference; private Drawable imageDrawable; @@ -33,7 +31,6 @@ public class CustomEmoji extends ReplacementSpan { CustomEmoji(WeakReference viewWeakReference) { Context mContext = viewWeakReference.get().getContext(); this.viewWeakReference = viewWeakReference; - view = viewWeakReference.get(); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(mContext); scale = sharedpreferences.getFloat(mContext.getString(R.string.SET_FONT_SCALE), 1.0f); } @@ -69,8 +66,6 @@ public class CustomEmoji extends ReplacementSpan { return new CustomTarget() { @Override public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { - Log.v(Helper.TAG, "resource: " + resource); - Log.v(Helper.TAG, "instanceof: " + (resource instanceof Animatable)); View view = viewWeakReference.get(); if (animate && resource instanceof Animatable) { Drawable.Callback callback = resource.getCallback(); 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 b85d10fe6..99be394d8 100644 --- a/app/src/main/java/app/fedilab/android/helper/Helper.java +++ b/app/src/main/java/app/fedilab/android/helper/Helper.java @@ -1739,4 +1739,13 @@ public class Helper { }).start(); } + + public static T getKeyByValue(Map map, E value) { + for (Map.Entry entry : map.entrySet()) { + if (Objects.equals(value, entry.getValue())) { + return entry.getKey(); + } + } + return null; + } } 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 56ab2f4e6..da408c1df 100644 --- a/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/SpannableHelper.java @@ -37,6 +37,7 @@ import android.text.Spanned; import android.text.TextPaint; import android.text.style.ClickableSpan; import android.text.style.URLSpan; +import android.util.Log; import android.util.Patterns; import android.view.LayoutInflater; import android.view.View; @@ -84,6 +85,7 @@ public class SpannableHelper { boolean convertHtml, WeakReference viewWeakReference) { + SpannableString initialContent; if (text == null) { return null; @@ -97,6 +99,8 @@ public class SpannableHelper { emojiList = status.emojis; } else if (account != null) { emojiList = account.emojis; + } else if (announcement != null) { + emojiList = announcement.emojis; } HashMap urlDetails = new HashMap<>(); if (convertHtml) { @@ -110,6 +114,7 @@ public class SpannableHelper { } if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains(" " + url); } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) @@ -162,10 +167,20 @@ public class SpannableHelper { if (content.toString().length() < matchEnd || matchStart < 0 || matchStart > matchEnd) { continue; } - final String url = content.toString().substring(matchStart, matchEnd); - if (!url.startsWith("http")) { - continue; + String url_temp = content.toString().substring(matchStart, matchEnd).replace("…", ""); + if (urlDetails.containsValue(url_temp + "…")) { + String originalURL = Helper.getKeyByValue(urlDetails, url_temp + "…"); + if (originalURL != null) { + content.replace(matchStart, matchEnd, originalURL); + offSetTruncate += (originalURL.length() - (url_temp.length() + 1)); + matchEnd = matchStart + originalURL.length(); + url_temp = originalURL; + } } + final String url = url_temp; + /* if (!url.startsWith("http")) { + continue; + }*/ String newURL = Helper.transformURL(context, url); //If URL has been transformed if (newURL.compareTo(url) != 0) { @@ -173,10 +188,12 @@ public class SpannableHelper { offSetTruncate += (newURL.length() - url.length()); matchEnd = matchStart + newURL.length(); //The transformed URL was in the list of URLs having a different names + if (urlDetails.containsKey(url)) { urlDetails.put(newURL, urlDetails.get(url)); } } + //Truncate URL if needed //TODO: add an option to disable truncated URLs String urlText = newURL; From 3cfade2f398c3e83dde38a8b0846bf5389280b8b Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 17:55:55 +0200 Subject: [PATCH 36/50] Fix focus point - Not apply it when fit preview is turned on --- .../android/ui/drawer/StatusAdapter.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 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 e6963e266..f396b439d 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 @@ -69,6 +69,7 @@ import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.request.RequestOptions; @@ -1004,10 +1005,13 @@ public class StatusAdapter extends RecyclerView.Adapter if (!mediaObfuscated(statusToDeal) || expand_media) { layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24); - Glide.with(layoutMediaBinding.media.getContext()) + RequestBuilder requestBuilder = Glide.with(layoutMediaBinding.media.getContext()) .load(statusToDeal.media_attachments.get(0).preview_url) - .apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))) - .into(layoutMediaBinding.media); + .apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))); + 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()) @@ -1055,11 +1059,13 @@ public class StatusAdapter extends RecyclerView.Adapter lp.setMargins(0, 0, (int) Helper.convertDpToPixel(5, context), 0); if (!mediaObfuscated(statusToDeal) || expand_media) { layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24); - Glide.with(layoutMediaBinding.media.getContext()) + RequestBuilder requestBuilder = Glide.with(layoutMediaBinding.media.getContext()) .load(attachment.preview_url) - .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) - .apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))) - .into(layoutMediaBinding.media); + .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context)))); + 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()) From c3a7bbadbd3dcf8b0f8e08dd015a5e1cc0c4c43b Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 18 Jul 2022 18:08:23 +0200 Subject: [PATCH 37/50] Fix issue #250 --- app/src/main/res/layout/fragment_login_join.xml | 1 + app/src/main/res/layout/fragment_login_register_mastodon.xml | 1 + app/src/main/res/values/styles.xml | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/fragment_login_join.xml b/app/src/main/res/layout/fragment_login_join.xml index 53cd1df89..352349dfd 100644 --- a/app/src/main/res/layout/fragment_login_join.xml +++ b/app/src/main/res/layout/fragment_login_join.xml @@ -86,6 +86,7 @@ diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 52027b6ca..98d258330 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -187,7 +187,7 @@ @color/cyanea_accent_dark_reference @color/cyanea_accent_dark_reference @color/cyanea_accent_dark_reference - @color/cyanea_accent_dark_reference + @color/cyanea_accent_dark_reference