Merge branch 'develop' into main

This commit is contained in:
Thomas 2022-07-19 18:39:03 +02:00
commit 92a9479c95
85 changed files with 2866 additions and 2098 deletions

View file

@ -1,7 +1,7 @@
[![Translation status](https://hosted.weblate.org/widgets/fedilab/-/strings/svg-badge.svg)](https://hosted.weblate.org/engage/fedilab/) [![Translation status](https://hosted.weblate.org/widgets/fedilab/-/strings/svg-badge.svg)](https://hosted.weblate.org/engage/fedilab/)
   [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)    [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
# Fedilab is a multi-accounts client for Mastodon, Pleroma, Peertube, GNU Social, Friendica and Pixelfed # Fedilab is a multi-accounts client for Mastodon, Pleroma, Friendica and Pixelfed
## Donate ## Donate
@ -16,7 +16,7 @@
[WIKI](https://fedilab.app/wiki/home/) [WIKI](https://fedilab.app/wiki/home/)
[Release notes](https://framagit.org/tom79/fedilab/tags) [Release notes](https://codeberg.org/tom79/Fedilab/tags)
Lead developer: [toot.fedilab.app/@apps](https://toot.fedilab.app/@apps) Lead developer: [toot.fedilab.app/@apps](https://toot.fedilab.app/@apps)

View file

@ -9,8 +9,8 @@ android {
defaultConfig { defaultConfig {
minSdk 21 minSdk 21
targetSdk 31 targetSdk 31
versionCode 396 versionCode 399
versionName "3.0.6" versionName "3.0.9"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
flavorDimensions "default" flavorDimensions "default"
@ -85,11 +85,9 @@ dependencies {
implementation 'com.github.GrenderG:Toasty:1.5.2' implementation 'com.github.GrenderG:Toasty:1.5.2'
implementation 'org.framagit.tom79:SparkButton:1.0.13' implementation 'org.framagit.tom79:SparkButton:1.0.13'
implementation "com.github.bumptech.glide:glide:4.12.0" 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.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: ':mytransl')
implementation project(path: ':ratethisapp') implementation project(path: ':ratethisapp')
@ -99,8 +97,7 @@ dependencies {
implementation project(path: ':cropper') implementation project(path: ':cropper')
annotationProcessor "com.github.bumptech.glide:compiler:4.12.0" annotationProcessor "com.github.bumptech.glide:compiler:4.12.0"
implementation 'jp.wasabeef:glide-transformations:4.3.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:glide-plugin:2.22.0'
implementation 'com.github.penfeizhou.android.animation:gif:2.22.0'
implementation 'com.google.android.exoplayer:exoplayer:2.16.1' implementation 'com.google.android.exoplayer:exoplayer:2.16.1'
implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation 'com.github.piasy:rxandroidaudio:1.7.0' implementation 'com.github.piasy:rxandroidaudio:1.7.0'

View file

@ -0,0 +1,730 @@
[
{
"code": "aa",
"language": "Afaraf"
},
{
"code": "ab",
"language": "аҧсуа бызшәа"
},
{
"code": "ae",
"language": "avesta"
},
{
"code": "af",
"language": "Afrikaans"
},
{
"code": "ak",
"language": "Akan"
},
{
"code": "am",
"language": "አማርኛ"
},
{
"code": "an",
"language": "aragonés"
},
{
"code": "ar",
"language": "اللغة العربية"
},
{
"code": "as",
"language": "অসমীয়া"
},
{
"code": "av",
"language": "авар мацӀ"
},
{
"code": "ay",
"language": "aymar aru"
},
{
"code": "az",
"language": "azərbaycan dili"
},
{
"code": "ba",
"language": "башҡорт теле"
},
{
"code": "be",
"language": "беларуская мова"
},
{
"code": "bg",
"language": "български език"
},
{
"code": "bh",
"language": "भोजपुरी"
},
{
"code": "bi",
"language": "Bislama"
},
{
"code": "bm",
"language": "bamanankan"
},
{
"code": "bn",
"language": "বাংলা"
},
{
"code": "bo",
"language": "བོད་ཡིག"
},
{
"code": "br",
"language": "brezhoneg"
},
{
"code": "bs",
"language": "bosanski jezik"
},
{
"code": "ca",
"language": "Català"
},
{
"code": "ce",
"language": "нохчийн мотт"
},
{
"code": "ch",
"language": "Chamoru"
},
{
"code": "co",
"language": "corsu"
},
{
"code": "cr",
"language": "ᓀᐦᐃᔭᐍᐏᐣ"
},
{
"code": "cs",
"language": "čeština"
},
{
"code": "cu",
"language": "ѩзыкъ словѣньскъ"
},
{
"code": "cv",
"language": "чӑваш чӗлхи"
},
{
"code": "cy",
"language": "Cymraeg"
},
{
"code": "da",
"language": "dansk"
},
{
"code": "de",
"language": "Deutsch"
},
{
"code": "dv",
"language": "Dhivehi"
},
{
"code": "dz",
"language": "རྫོང་ཁ"
},
{
"code": "ee",
"language": "Eʋegbe"
},
{
"code": "el",
"language": "Ελληνικά"
},
{
"code": "en",
"language": "English"
},
{
"code": "eo",
"language": "Esperanto"
},
{
"code": "es",
"language": "Español"
},
{
"code": "et",
"language": "eesti"
},
{
"code": "eu",
"language": "euskara"
},
{
"code": "fa",
"language": "فارسی"
},
{
"code": "ff",
"language": "Fulfulde"
},
{
"code": "fi",
"language": "suomi"
},
{
"code": "fj",
"language": "Vakaviti"
},
{
"code": "fo",
"language": "føroyskt"
},
{
"code": "fr",
"language": "Français"
},
{
"code": "fy",
"language": "Frysk"
},
{
"code": "ga",
"language": "Gaeilge"
},
{
"code": "gd",
"language": "Gàidhlig"
},
{
"code": "gl",
"language": "galego"
},
{
"code": "gu",
"language": "ગુજરાતી"
},
{
"code": "gv",
"language": "Gaelg"
},
{
"code": "ha",
"language": "هَوُسَ"
},
{
"code": "he",
"language": "עברית"
},
{
"code": "hi",
"language": "हिन्दी"
},
{
"code": "ho",
"language": "Hiri Motu"
},
{
"code": "hr",
"language": "Hrvatski"
},
{
"code": "ht",
"language": "Kreyòl ayisyen"
},
{
"code": "hu",
"language": "magyar"
},
{
"code": "hy",
"language": "Հայերեն"
},
{
"code": "hz",
"language": "Otjiherero"
},
{
"code": "ia",
"language": "Interlingua"
},
{
"code": "id",
"language": "Bahasa Indonesia"
},
{
"code": "ie",
"language": "Interlingue"
},
{
"code": "ig",
"language": "Asụsụ Igbo"
},
{
"code": "ii",
"language": "ꆈꌠ꒿ Nuosuhxop"
},
{
"code": "ik",
"language": "Iñupiaq"
},
{
"code": "io",
"language": "Ido"
},
{
"code": "is",
"language": "Íslenska"
},
{
"code": "it",
"language": "Italiano"
},
{
"code": "iu",
"language": "ᐃᓄᒃᑎᑐᑦ"
},
{
"code": "ja",
"language": "日本語"
},
{
"code": "jv",
"language": "basa Jawa"
},
{
"code": "ka",
"language": "ქართული"
},
{
"code": "kg",
"language": "Kikongo"
},
{
"code": "ki",
"language": "Gĩkũyũ"
},
{
"code": "kj",
"language": "Kuanyama"
},
{
"code": "kk",
"language": "қазақ тілі"
},
{
"code": "kl",
"language": "kalaallisut"
},
{
"code": "km",
"language": "ខេមរភាសា"
},
{
"code": "kn",
"language": "ಕನ್ನಡ"
},
{
"code": "ko",
"language": "한국어"
},
{
"code": "kr",
"language": "Kanuri"
},
{
"code": "ks",
"language": "कश्मीरी"
},
{
"code": "ku",
"language": "Kurmancî"
},
{
"code": "kv",
"language": "коми кыв"
},
{
"code": "kw",
"language": "Kernewek"
},
{
"code": "ky",
"language": "Кыргызча"
},
{
"code": "la",
"language": "latine"
},
{
"code": "lb",
"language": "Lëtzebuergesch"
},
{
"code": "lg",
"language": "Luganda"
},
{
"code": "li",
"language": "Limburgs"
},
{
"code": "ln",
"language": "Lingála"
},
{
"code": "lo",
"language": "ພາສາ"
},
{
"code": "lt",
"language": "lietuvių kalba"
},
{
"code": "lu",
"language": "Tshiluba"
},
{
"code": "lv",
"language": "latviešu valoda"
},
{
"code": "mg",
"language": "fiteny malagasy"
},
{
"code": "mh",
"language": "Kajin M̧ajeļ"
},
{
"code": "mi",
"language": "te reo Māori"
},
{
"code": "mk",
"language": "македонски јазик"
},
{
"code": "ml",
"language": "മലയാളം"
},
{
"code": "mn",
"language": "Монгол хэл"
},
{
"code": "mr",
"language": "मराठी"
},
{
"code": "ms",
"language": "Bahasa Melayu"
},
{
"code": "mt",
"language": "Malti"
},
{
"code": "my",
"language": "ဗမာစာ"
},
{
"code": "na",
"language": "Ekakairũ Naoero"
},
{
"code": "nb",
"language": "Norsk bokmål"
},
{
"code": "nd",
"language": "isiNdebele"
},
{
"code": "ne",
"language": "नेपाली"
},
{
"code": "ng",
"language": "Owambo"
},
{
"code": "nl",
"language": "Nederlands"
},
{
"code": "nn",
"language": "Norsk Nynorsk"
},
{
"code": "no",
"language": "Norsk"
},
{
"code": "nr",
"language": "isiNdebele"
},
{
"code": "nv",
"language": "Diné bizaad"
},
{
"code": "ny",
"language": "chiCheŵa"
},
{
"code": "oc",
"language": "occitan"
},
{
"code": "oj",
"language": "ᐊᓂᔑᓈᐯᒧᐎᓐ"
},
{
"code": "om",
"language": "Afaan Oromoo"
},
{
"code": "or",
"language": "ଓଡ଼ିଆ"
},
{
"code": "os",
"language": "ирон æвзаг"
},
{
"code": "pa",
"language": "ਪੰਜਾਬੀ"
},
{
"code": "pi",
"language": "पाऴि"
},
{
"code": "pl",
"language": "Polski"
},
{
"code": "ps",
"language": "پښتو"
},
{
"code": "pt",
"language": "Português"
},
{
"code": "qu",
"language": "Runa Simi"
},
{
"code": "rm",
"language": "rumantsch grischun"
},
{
"code": "rn",
"language": "Ikirundi"
},
{
"code": "ro",
"language": "Română"
},
{
"code": "ru",
"language": "Русский"
},
{
"code": "rw",
"language": "Ikinyarwanda"
},
{
"code": "sa",
"language": "संस्कृतम्"
},
{
"code": "sc",
"language": "sardu"
},
{
"code": "sd",
"language": "सिन्धी"
},
{
"code": "se",
"language": "Davvisámegiella"
},
{
"code": "sg",
"language": "yângâ tî sängö"
},
{
"code": "si",
"language": "සිංහල"
},
{
"code": "sk",
"language": "slovenčina"
},
{
"code": "sl",
"language": "slovenščina"
},
{
"code": "sn",
"language": "chiShona"
},
{
"code": "so",
"language": "Soomaaliga"
},
{
"code": "sq",
"language": "Shqip"
},
{
"code": "sr",
"language": "српски језик"
},
{
"code": "ss",
"language": "SiSwati"
},
{
"code": "st",
"language": "Sesotho"
},
{
"code": "su",
"language": "Basa Sunda"
},
{
"code": "sv",
"language": "Svenska"
},
{
"code": "sw",
"language": "Kiswahili"
},
{
"code": "ta",
"language": "தமிழ்"
},
{
"code": "te",
"language": "తెలుగు"
},
{
"code": "tg",
"language": "тоҷикӣ"
},
{
"code": "th",
"language": "ไทย"
},
{
"code": "ti",
"language": "ትግርኛ"
},
{
"code": "tk",
"language": "Türkmen"
},
{
"code": "tl",
"language": "Wikang Tagalog"
},
{
"code": "tn",
"language": "Setswana"
},
{
"code": "to",
"language": "faka Tonga"
},
{
"code": "tr",
"language": "Türkçe"
},
{
"code": "ts",
"language": "Xitsonga"
},
{
"code": "tt",
"language": "татар теле"
},
{
"code": "tw",
"language": "Twi"
},
{
"code": "ty",
"language": "Reo Tahiti"
},
{
"code": "ug",
"language": "ئۇيغۇرچە‎"
},
{
"code": "uk",
"language": "Українська"
},
{
"code": "ur",
"language": "اردو"
},
{
"code": "uz",
"language": "Ўзбек"
},
{
"code": "ve",
"language": "Tshivenḓa"
},
{
"code": "vi",
"language": "Tiếng Việt"
},
{
"code": "vo",
"language": "Volapük"
},
{
"code": "wa",
"language": "walon"
},
{
"code": "wo",
"language": "Wollof"
},
{
"code": "xh",
"language": "isiXhosa"
},
{
"code": "yi",
"language": "ייִדיש"
},
{
"code": "yo",
"language": "Yorùbá"
},
{
"code": "za",
"language": "Saɯ cueŋƅ"
},
{
"code": "zh",
"language": "中文"
},
{
"code": "zu",
"language": "isiZulu"
}
]

View file

@ -8,7 +8,7 @@
"accent": "#FF2b90d9", "accent": "#FF2b90d9",
"accent_dark": "#FF1b80c9", "accent_dark": "#FF1b80c9",
"accent_light": "#FF772b90d9", "accent_light": "#FF772b90d9",
"background": "#FF121212", "background": "#FF272727",
"background_dark": "#FF282c37", "background_dark": "#FF282c37",
"background_light": "#FF282c37", "background_light": "#FF282c37",
"should_tint_statusbar": true, "should_tint_statusbar": true,

View file

@ -537,7 +537,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24); headerMainBinding.ownerAccounts.setImageResource(R.drawable.ic_baseline_arrow_drop_up_24);
new Thread(() -> { new Thread(() -> {
try { try {
List<BaseAccount> accounts = new Account(BaseMainActivity.this).getAll(); List<BaseAccount> accounts = new Account(BaseMainActivity.this).getCrossAccounts();
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
binding.navView.getMenu().clear(); binding.navView.getMenu().clear();
@ -772,7 +772,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
currentAccount.mastodon_account.display_name = currentAccount.mastodon_account.acct; currentAccount.mastodon_account.display_name = currentAccount.mastodon_account.acct;
} }
headerMainBinding.accountName.setText(currentAccount.mastodon_account.display_name); 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); MastodonHelper.loadProfileMediaMastodon(headerMainBinding.backgroundImage, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER);
/* /*
* Some general data are loaded when the app starts such; * Some general data are loaded when the app starts such;
@ -786,7 +786,12 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
new ViewModelProvider(BaseMainActivity.this).get(InstancesVM.class).getEmoji(currentInstance); new ViewModelProvider(BaseMainActivity.this).get(InstancesVM.class).getEmoji(currentInstance);
//Retrieve instance info //Retrieve instance info
new ViewModelProvider(BaseMainActivity.this).get(InstancesVM.class).getInstance(currentInstance) 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 //Retrieve filters
new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getFilters(currentInstance, currentToken) new ViewModelProvider(BaseMainActivity.this).get(AccountsVM.class).getFilters(currentInstance, currentToken)
.observe(BaseMainActivity.this, filters -> mainFilters = filters); .observe(BaseMainActivity.this, filters -> mainFilters = filters);
@ -850,7 +855,7 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
binding.toolbarSearch.setOnSearchClickListener(v -> binding.tabLayout.setVisibility(View.VISIBLE)); binding.toolbarSearch.setOnSearchClickListener(v -> binding.tabLayout.setVisibility(View.VISIBLE));
//For receiving data from other activities //For receiving data from other activities
LocalBroadcastManager.getInstance(BaseMainActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA)); LocalBroadcastManager.getInstance(BaseMainActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance)) { if (emojis == null || !emojis.containsKey(BaseMainActivity.currentInstance) || emojis.get(BaseMainActivity.currentInstance) == null) {
new Thread(() -> { new Thread(() -> {
try { try {
emojis.put(currentInstance, new EmojiInstance(BaseMainActivity.this).getEmojiList(BaseMainActivity.currentInstance)); emojis.put(currentInstance, new EmojiInstance(BaseMainActivity.this).getEmojiList(BaseMainActivity.currentInstance));
@ -1059,11 +1064,6 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
} }
@Override
protected void onResume() {
super.onResume();
}
public void redrawPinned(List<MastodonList> mastodonLists) { public void redrawPinned(List<MastodonList> mastodonLists) {
int currentItem = binding.viewPager.getCurrentItem(); int currentItem = binding.viewPager.getCurrentItem();
new ViewModelProvider(BaseMainActivity.this).get(TopBarVM.class).getDBPinned() new ViewModelProvider(BaseMainActivity.this).get(TopBarVM.class).getDBPinned()

View file

@ -22,8 +22,6 @@ import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.LinkMovementMethod; 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.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -105,14 +104,7 @@ public class AdminAccountActivity extends BaseActivity {
} }
binding.toolbar.setPopupTheme(Helper.popupStyle()); binding.toolbar.setPopupTheme(Helper.popupStyle());
if (account != null) { if (account != null) {
new Thread(() -> { initializeView(account);
account = SpannableHelper.convertAccount(AdminAccountActivity.this, account);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeView(account);
mainHandler.post(myRunnable);
}).start();
} else { } else {
Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
@ -314,7 +306,10 @@ 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)),
TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setText(String.format("@%s", account.acct));
binding.accountUn.setOnLongClickListener(v -> { binding.accountUn.setOnLongClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

View file

@ -22,8 +22,6 @@ import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.LinkMovementMethod; 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.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -105,14 +104,7 @@ public class AdminReportActivity extends BaseActivity {
} }
binding.toolbar.setPopupTheme(Helper.popupStyle()); binding.toolbar.setPopupTheme(Helper.popupStyle());
if (account != null) { if (account != null) {
new Thread(() -> { initializeView(account);
account = SpannableHelper.convertAccount(AdminReportActivity.this, account);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeView(account);
mainHandler.post(myRunnable);
}).start();
} else { } else {
Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show();
finish(); finish();
@ -331,8 +323,10 @@ public class AdminReportActivity extends BaseActivity {
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
} }
binding.accountDn.setText(
binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE); account.getSpanDisplayName(AdminReportActivity.this,
new WeakReference<>(binding.accountDn)),
TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setText(String.format("@%s", account.acct));
binding.accountUn.setOnLongClickListener(v -> { binding.accountUn.setOnLongClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

View file

@ -26,6 +26,7 @@ import android.content.BroadcastReceiver;
import android.content.ClipData; import android.content.ClipData;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
@ -48,6 +49,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.work.Data; import androidx.work.Data;
import androidx.work.OneTimeWorkRequest; import androidx.work.OneTimeWorkRequest;
@ -69,10 +71,12 @@ import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Context; import app.fedilab.android.client.entities.api.Context;
import app.fedilab.android.client.entities.api.EmojiInstance; 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.Mention;
import app.fedilab.android.client.entities.api.ScheduledStatus; import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.BaseAccount; import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.Languages;
import app.fedilab.android.client.entities.app.StatusDraft; import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityPaginationBinding; import app.fedilab.android.databinding.ActivityPaginationBinding;
import app.fedilab.android.databinding.PopupContactBinding; import app.fedilab.android.databinding.PopupContactBinding;
@ -81,7 +85,6 @@ import app.fedilab.android.helper.DividerDecorationSimple;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.MediaHelper; import app.fedilab.android.helper.MediaHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.interfaces.OnDownloadInterface; import app.fedilab.android.interfaces.OnDownloadInterface;
import app.fedilab.android.jobs.ScheduleThreadWorker; import app.fedilab.android.jobs.ScheduleThreadWorker;
@ -100,7 +103,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
public static final int REQUEST_AUDIO_PERMISSION_RESULT = 1653; public static final int REQUEST_AUDIO_PERMISSION_RESULT = 1653;
public static final int PICK_MEDIA = 5700; public static final int PICK_MEDIA = 5700;
public static final int TAKE_PHOTO = 5600; public static final int TAKE_PHOTO = 5600;
private final Timer timer = new Timer();
private List<Status> statusList; private List<Status> statusList;
private Status statusReply, statusMention; private Status statusReply, statusMention;
private StatusDraft statusDraft; private StatusDraft statusDraft;
@ -234,6 +237,14 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
}).start(); }).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); StatusesVM statusesVM = new ViewModelProvider(ComposeActivity.this).get(StatusesVM.class);
//Empty compose //Empty compose
List<Status> statusDraftList = new ArrayList<>(); List<Status> statusDraftList = new ArrayList<>();
@ -257,12 +268,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
}); });
} else if (statusDraft != null) {//Restore a draft with all messages } 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) { if (statusDraft.statusReplyList != null) {
statusList.addAll(statusDraft.statusReplyList); statusList.addAll(statusDraft.statusReplyList);
binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList)); binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList));
@ -275,22 +280,15 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter); binding.recyclerView.setAdapter(composeAdapter);
binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1); binding.recyclerView.scrollToPosition(composeAdapter.getItemCount() - 1);
};
mainHandler.post(myRunnable);
}).start();
} else if (statusReply != null) { } else if (statusReply != null) {
new Thread(() -> {
statusReply = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusReply);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
statusList.add(statusReply); statusList.add(statusReply);
int statusCount = statusList.size(); int statusCount = statusList.size();
statusDraftList.get(0).in_reply_to_id = statusReply.id; statusDraftList.get(0).in_reply_to_id = statusReply.id;
//We change order for mentions //We change order for mentions
//At first place the account that has been mentioned if it's not our //At first place the account that has been mentioned if it's not our
statusDraftList.get(0).mentions = new ArrayList<>(); statusDraftList.get(0).mentions = new ArrayList<>();
if (!statusReply.account.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) { if (statusReply.account.acct != null && !statusReply.account.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) {
Mention mention = new Mention(); Mention mention = new Mention();
mention.acct = "@" + statusReply.account.acct; mention.acct = "@" + statusReply.account.acct;
mention.url = statusReply.account.url; mention.url = statusReply.account.url;
@ -301,7 +299,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
//There are other mentions to //There are other mentions to
if (statusReply.mentions != null && statusReply.mentions.size() > 0) { if (statusReply.mentions != null && statusReply.mentions.size() > 0) {
for (Mention mentionTmp : statusReply.mentions) { for (Mention mentionTmp : statusReply.mentions) {
if (!mentionTmp.acct.equalsIgnoreCase(statusReply.account.acct) && !mentionTmp.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) { if (statusReply.account.acct != null && !mentionTmp.acct.equalsIgnoreCase(statusReply.account.acct) && !mentionTmp.acct.equalsIgnoreCase(currentAccount.mastodon_account.acct)) {
statusDraftList.get(0).mentions.add(mentionTmp); statusDraftList.get(0).mentions.add(mentionTmp);
} }
} }
@ -334,9 +332,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
binding.recyclerView.setAdapter(composeAdapter); binding.recyclerView.setAdapter(composeAdapter);
statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id) statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id)
.observe(ComposeActivity.this, this::initializeContextView); .observe(ComposeActivity.this, this::initializeContextView);
};
mainHandler.post(myRunnable);
}).start();
} else { } else {
//Compose without replying //Compose without replying
statusList.addAll(statusDraftList); statusList.addAll(statusDraftList);
@ -348,19 +343,20 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
if (statusMention != null) { if (statusMention != null) {
composeAdapter.loadMentions(statusMention); composeAdapter.loadMentions(statusMention);
} }
} }
MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account); MastodonHelper.loadPPMastodon(binding.profilePicture, account.mastodon_account);
LocalBroadcastManager.getInstance(this) LocalBroadcastManager.getInstance(this)
.registerReceiver(imageReceiver, .registerReceiver(imageReceiver,
new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE)); new IntentFilter(Helper.INTENT_SEND_MODIFIED_IMAGE));
new Timer().scheduleAtFixedRate(new TimerTask() { if (timer != null) {
timer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
storeDraft(false); storeDraft(false);
} }
}, 0, 10000); }, 0, 10000);
}
if (sharedUriList != null && sharedUriList.size() > 0) { if (sharedUriList != null && sharedUriList.size() > 0) {
@ -395,6 +391,9 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (timer != null) {
timer.cancel();
}
LocalBroadcastManager.getInstance(this) LocalBroadcastManager.getInstance(this)
.unregisterReceiver(imageReceiver); .unregisterReceiver(imageReceiver);
} }
@ -579,6 +578,46 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} else { } else {
Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show(); Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show();
} }
} else if (item.getItemId() == R.id.action_language) {
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this);
List<Languages.Language> languages = Languages.get(ComposeActivity.this);
String[] codesArr = new String[0];
String[] languagesArr = new String[0];
String currentCode = sharedpreferences.getString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, null);
int selection = 0;
if (languages != null) {
codesArr = new String[languages.size()];
languagesArr = new String[languages.size()];
int i = 0;
for (Languages.Language language : languages) {
codesArr[i] = language.code;
languagesArr[i] = language.language;
if (currentCode != null && currentCode.equalsIgnoreCase(language.code)) {
selection = i;
}
i++;
}
}
SharedPreferences.Editor editor = sharedpreferences.edit();
AlertDialog.Builder builder = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
builder.setTitle(getString(R.string.message_language));
builder.setSingleChoiceItems(languagesArr, selection, null);
String[] finalCodesArr = codesArr;
builder.setPositiveButton(R.string.validate, (dialog, which) -> {
int selectedPosition = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
editor.putString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, finalCodesArr[selectedPosition]);
editor.apply();
dialog.dismiss();
});
builder.setNegativeButton(R.string.reset, (dialog, which) -> {
editor.putString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, null);
editor.apply();
dialog.dismiss();
});
builder.create().show();
} }
return true; return true;
} }
@ -667,6 +706,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} else { } else {
statusReplies.add(status); statusReplies.add(status);
} }
} }
if (statusDraft == null) { if (statusDraft == null) {
statusDraft = new StatusDraft(ComposeActivity.this); statusDraft = new StatusDraft(ComposeActivity.this);
@ -677,10 +717,12 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
} }
if (statusReplies.size() > 0) { if (statusReplies.size() > 0) {
statusDraft.statusReplyList = statusReplies; statusDraft.statusReplyList = new ArrayList<>();
statusDraft.statusReplyList.addAll(statusReplies);
} }
if (statusDrafts.size() > 0) { if (statusDrafts.size() > 0) {
statusDraft.statusDraftList = statusDrafts; statusDraft.statusDraftList = new ArrayList<>();
statusDraft.statusDraftList.addAll(statusDrafts);
} }
if (statusDraft.instance == null) { if (statusDraft.instance == null) {
statusDraft.instance = account.instance; statusDraft.instance = account.instance;
@ -750,6 +792,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft); intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft);
intent.putExtra(Helper.ARG_INSTANCE, instance); intent.putExtra(Helper.ARG_INSTANCE, instance);
intent.putExtra(Helper.ARG_TOKEN, token); intent.putExtra(Helper.ARG_TOKEN, token);
intent.putExtra(Helper.ARG_USER_ID, account.user_id);
intent.putExtra(Helper.ARG_SCHEDULED_DATE, scheduledDate); intent.putExtra(Helper.ARG_SCHEDULED_DATE, scheduledDate);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent); startForegroundService(intent);
@ -757,7 +800,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
startService(intent); startService(intent);
} }
} else { } else {
new ThreadMessageService(ComposeActivity.this, instance, token, statusDraft, scheduledDate); new ThreadMessageService(ComposeActivity.this, instance, account.user_id, token, statusDraft, scheduledDate);
} }
finish(); finish();
} }

View file

@ -43,7 +43,6 @@ import app.fedilab.android.databinding.ActivityConversationBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext;
import app.fedilab.android.viewmodel.mastodon.StatusesVM; import app.fedilab.android.viewmodel.mastodon.StatusesVM;
@ -82,21 +81,14 @@ public class ContextActivity extends BaseActivity {
focusedStatus = null; // or other values focusedStatus = null; // or other values
if (b != null) if (b != null)
focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS); 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(); finish();
return; return;
} }
MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account); MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
Bundle bundle = new Bundle(); 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); bundle.putSerializable(Helper.ARG_STATUS, focusedStatus);
currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null); currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null);
};
mainHandler.post(myRunnable);
}).start();
StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class); StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class);
timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> { timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> {
if (status != null) { if (status != null) {
@ -168,10 +160,4 @@ public class ContextActivity extends BaseActivity {
return true; return true;
} }
@Override
protected void onDestroy() {
super.onDestroy();
binding = null;
currentFragment = null;
}
} }

View file

@ -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 @Override
public void onAllDeleted() { public void onAllDeleted() {

View file

@ -294,7 +294,6 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
@Override @Override
public void onDestroy() { public void onDestroy() {
binding = null;
unregisterReceiver(onDownloadComplete); unregisterReceiver(onDownloadComplete);
super.onDestroy(); super.onDestroy();
} }

View file

@ -27,11 +27,7 @@ import android.content.SharedPreferences;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
@ -64,6 +60,7 @@ import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -148,8 +145,6 @@ public class ProfileActivity extends BaseActivity {
account_id = b.getString(Helper.ARG_USER_ID, null); account_id = b.getString(Helper.ARG_USER_ID, null);
mention_str = b.getString(Helper.ARG_MENTION, null); mention_str = b.getString(Helper.ARG_MENTION, null);
} }
postponeEnterTransition(); postponeEnterTransition();
//Remove title //Remove title
@ -165,14 +160,7 @@ public class ProfileActivity extends BaseActivity {
binding.toolbar.setPopupTheme(Helper.popupStyle()); binding.toolbar.setPopupTheme(Helper.popupStyle());
accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class); accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class);
if (account != null) { if (account != null) {
new Thread(() -> { initializeView(account);
account = SpannableHelper.convertAccount(ProfileActivity.this, account);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> initializeView(account);
mainHandler.post(myRunnable);
}).start();
} else if (account_id != null) { } else if (account_id != null) {
accountsVM.getAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account_id).observe(ProfileActivity.this, fetchedAccount -> { accountsVM.getAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account_id).observe(ProfileActivity.this, fetchedAccount -> {
account = fetchedAccount; account = fetchedAccount;
@ -359,11 +347,11 @@ public class ProfileActivity extends BaseActivity {
binding.fieldsContainer.setAdapter(fieldAdapter); binding.fieldsContainer.setAdapter(fieldAdapter);
binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this)); binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this));
} }
if (account.span_display_name == null && account.display_name == null) {
binding.accountDn.setText(account.username); binding.accountDn.setText(
} else { account.getSpanDisplayName(ProfileActivity.this,
binding.accountDn.setText(account.span_display_name != null ? account.span_display_name : account.display_name, TextView.BufferType.SPANNABLE); new WeakReference<>(binding.accountDn)),
} TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setText(String.format("@%s", account.acct));
binding.accountUn.setOnLongClickListener(v -> { binding.accountUn.setOnLongClickListener(v -> {
@ -377,12 +365,10 @@ public class ProfileActivity extends BaseActivity {
clipboard.setPrimaryClip(clip); clipboard.setPrimaryClip(clip);
return false; return false;
}); });
binding.accountNote.setText(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) account.getSpanNote(ProfileActivity.this,
binding.accountNote.setText(account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note, Html.FROM_HTML_MODE_COMPACT)), TextView.BufferType.SPANNABLE); new WeakReference<>(binding.accountNote)),
else TextView.BufferType.SPANNABLE);
binding.accountNote.setText(account.span_note != null ? account.span_note : new SpannableString(Html.fromHtml(account.note)), TextView.BufferType.SPANNABLE);
binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance());
MastodonHelper.loadPPMastodon(binding.accountPp, account); MastodonHelper.loadPPMastodon(binding.accountPp, account);

View file

@ -53,6 +53,7 @@ public class SearchResultTabActivity extends BaseActivity {
private String search; private String search;
private ActivitySearchResultTabsBinding binding; private ActivitySearchResultTabsBinding binding;
private TabLayout.Tab initial;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { 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))); getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
setTitle(search); 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.accounts)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.action_cache))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.action_cache)));
@ -125,7 +127,6 @@ public class SearchResultTabActivity extends BaseActivity {
@Override @Override
public boolean onQueryTextSubmit(String query) { public boolean onQueryTextSubmit(String query) {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(binding.searchTabLayout.getWindowToken(), 0); imm.hideSoftInputFromWindow(binding.searchTabLayout.getWindowToken(), 0);
query = query.replaceAll("^#+", ""); query = query.replaceAll("^#+", "");
search = query.trim(); search = query.trim();
@ -134,6 +135,7 @@ public class SearchResultTabActivity extends BaseActivity {
searchView.clearFocus(); searchView.clearFocus();
setTitle(search); setTitle(search);
searchView.setIconified(true); searchView.setIconified(true);
binding.searchTabLayout.selectTab(initial);
return false; return false;
} }

View file

@ -16,6 +16,7 @@ package app.fedilab.android.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
@ -62,6 +63,7 @@ public class SettingsActivity extends BaseActivity {
} }
canGoBack = false; canGoBack = false;
binding.setAccount.setOnClickListener(v -> displaySettings(SettingsEnum.ACCOUNT));
binding.setTimelines.setOnClickListener(v -> displaySettings(SettingsEnum.TIMELINES)); binding.setTimelines.setOnClickListener(v -> displaySettings(SettingsEnum.TIMELINES));
binding.setNotifications.setOnClickListener(v -> displaySettings(SettingsEnum.NOTIFICATIONS)); binding.setNotifications.setOnClickListener(v -> displaySettings(SettingsEnum.NOTIFICATIONS));
binding.setInterface.setOnClickListener(v -> displaySettings(SettingsEnum.INTERFACE)); binding.setInterface.setOnClickListener(v -> displaySettings(SettingsEnum.INTERFACE));
@ -79,6 +81,10 @@ public class SettingsActivity extends BaseActivity {
public void displaySettings(SettingsEnum settingsEnum) { public void displaySettings(SettingsEnum settingsEnum) {
if (settingsEnum == SettingsEnum.ACCOUNT) {
Intent intent = new Intent(SettingsActivity.this, EditProfileActivity.class);
startActivity(intent);
} else {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
FragmentManager fragmentManager = getSupportFragmentManager(); FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = FragmentTransaction fragmentTransaction =
@ -135,6 +141,7 @@ public class SettingsActivity extends BaseActivity {
fragmentTransaction.commit(); fragmentTransaction.commit();
}); });
} }
}
@Override @Override
@ -156,15 +163,6 @@ public class SettingsActivity extends BaseActivity {
} }
@Override
protected void onDestroy() {
super.onDestroy();
if (currentFragment != null) {
currentFragment.onDestroy();
}
binding = null;
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
@ -177,6 +175,8 @@ public class SettingsActivity extends BaseActivity {
public enum SettingsEnum { public enum SettingsEnum {
@SerializedName("ACCOUNT")
ACCOUNT("ACCOUNT"),
@SerializedName("TIMELINES") @SerializedName("TIMELINES")
TIMELINES("TIMELINES"), TIMELINES("TIMELINES"),
@SerializedName("NOTIFICATIONS") @SerializedName("NOTIFICATIONS")

View file

@ -0,0 +1,40 @@
package app.fedilab.android.client.endpoints;
/* 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 <http://www.gnu.org/licenses>. */
import retrofit2.Call;
import retrofit2.http.DELETE;
import retrofit2.http.Header;
import retrofit2.http.PUT;
import retrofit2.http.Path;
public interface PleromaAPI {
@PUT("pleroma/statuses/{id}/reactions/{name}")
Call<Void> addReaction(
@Header("Authorization") String app_token,
@Path("id") String id,
@Path("name") String name
);
@DELETE("pleroma/statuses/{id}/reactions/{name}")
Call<Void> removeReaction(
@Header("Authorization") String app_token,
@Path("id") String id,
@Path("name") String name
);
}

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.text.Spannable; import android.text.Spannable;
import android.view.View;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import app.fedilab.android.helper.SpannableHelper;
public class Account implements Serializable { public class Account implements Serializable {
@SerializedName("id") @SerializedName("id")
@ -74,11 +79,21 @@ public class Account implements Serializable {
@SerializedName("moved") @SerializedName("moved")
public Account moved; public Account moved;
//Some extra spannable element - They will be filled automatically when fetching the account public synchronized Spannable getSpanDisplayName(Context context, WeakReference<View> viewWeakReference) {
public transient Spannable span_display_name; if (display_name == null || display_name.isEmpty()) {
public transient Spannable span_note; display_name = username;
}
return SpannableHelper.convert(context, display_name, null, this, null, true, viewWeakReference);
}
public synchronized Spannable getSpanNote(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, note, null, this, null, true, viewWeakReference);
}
public transient RelationShip relationShip; public transient RelationShip relationShip;
public static class AccountParams implements Serializable { public static class AccountParams implements Serializable {
@SerializedName("discoverable") @SerializedName("discoverable")
public boolean discoverable; public boolean discoverable;

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.text.Spannable; import android.text.Spannable;
import android.view.View;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import app.fedilab.android.helper.SpannableHelper;
public class Announcement { public class Announcement {
@SerializedName("id") @SerializedName("id")
public String id; public String id;
@ -49,6 +54,9 @@ public class Announcement {
@SerializedName("reactions") @SerializedName("reactions")
public List<Reaction> reactions; public List<Reaction> reactions;
//Some extra spannable element - They will be filled automatically when fetching the status
public transient Spannable span_content; public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, content, null, null, this, true, viewWeakReference);
}
} }

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.text.Spannable; 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 com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import app.fedilab.android.R;
import app.fedilab.android.helper.SpannableHelper;
public class Field implements Serializable { public class Field implements Serializable {
@SerializedName("name") @SerializedName("name")
public String name; public String name;
@ -30,7 +39,19 @@ public class Field implements Serializable {
public Date verified_at; public Date verified_at;
//Some extra spannable element - They will be filled automatically when fetching the account //Some extra spannable element - They will be filled automatically when fetching the account
public transient Spannable value_span; private transient ForegroundColorSpan value_span;
public synchronized Spannable getValueSpan(Context context, Account account, WeakReference<View> 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 { public static class FieldParams implements Serializable {
@SerializedName("name") @SerializedName("name")

View file

@ -1,5 +1,6 @@
package app.fedilab.android.client.entities.api; package app.fedilab.android.client.entities.api;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
@ -113,6 +114,25 @@ public class Instance implements Serializable {
return mimeTypes; 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 { public static class Configuration implements Serializable {
@SerializedName("statuses") @SerializedName("statuses")
public StatusesConf statusesConf; public StatusesConf statusesConf;

View file

@ -29,6 +29,8 @@ public class Notification {
public String type; public String type;
@SerializedName("created_at") @SerializedName("created_at")
public Date created_at; public Date created_at;
@SerializedName("emoji")
public String emoji;
@SerializedName("account") @SerializedName("account")
public Account account; public Account account;
@SerializedName("status") @SerializedName("status")

View file

@ -0,0 +1,25 @@
package app.fedilab.android.client.entities.api;
/* 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 <http://www.gnu.org/licenses>. */
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.List;
public class Pleroma implements Serializable {
@SerializedName("emoji_reactions")
public List<Reaction> emoji_reactions;
}

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.text.Spannable; import android.text.Spannable;
import android.view.View;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import app.fedilab.android.helper.SpannableHelper;
public class Poll implements Serializable { public class Poll implements Serializable {
@SerializedName("id") @SerializedName("id")
@ -53,7 +58,11 @@ public class Poll implements Serializable {
@SerializedName("votes_count") @SerializedName("votes_count")
public int 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 Spannable span_title;
public Spannable getSpanTitle(Context context, Status status, WeakReference<View> viewWeakReference) {
span_title = SpannableHelper.convert(context, title, status, null, null, true, viewWeakReference);
return span_title;
}
} }
} }

View file

@ -16,7 +16,9 @@ package app.fedilab.android.client.entities.api;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
public class Reaction { import java.io.Serializable;
public class Reaction implements Serializable {
@SerializedName("name") @SerializedName("name")
public String name; public String name;
@SerializedName("count") @SerializedName("count")

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.text.Spannable; import android.text.Spannable;
import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import app.fedilab.android.helper.SpannableHelper;
public class Status implements Serializable, Cloneable { public class Status implements Serializable, Cloneable {
@SerializedName("id") @SerializedName("id")
@ -84,14 +89,12 @@ public class Status implements Serializable, Cloneable {
public Card card; public Card card;
@SerializedName("poll") @SerializedName("poll")
public Poll poll; public Poll poll;
@SerializedName("pleroma")
public Pleroma pleroma;
public Attachment art_attachment; 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 boolean isExpended = false; public boolean isExpended = false;
public boolean isTruncated = true; public boolean isTruncated = true;
public boolean isFetchMore = false; public boolean isFetchMore = false;
@ -105,6 +108,24 @@ public class Status implements Serializable, Cloneable {
public transient boolean setCursorToEnd = false; public transient boolean setCursorToEnd = false;
public transient int cursorPosition = 0; public transient int cursorPosition = 0;
public transient boolean submitted = false; public transient boolean submitted = false;
//Some extra spannable element - They will be filled automatically when fetching the status
public synchronized Spannable getSpanContent(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, content, this, null, null, true, viewWeakReference);
}
public Spannable getSpanContentNitter() {
return SpannableHelper.convertNitter(content);
}
public synchronized Spannable getSpanSpoiler(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, spoiler_text, this, null, null, true, viewWeakReference);
}
public synchronized Spannable getSpanTranslate(Context context, WeakReference<View> viewWeakReference) {
return SpannableHelper.convert(context, translationContent, this, null, null, true, viewWeakReference);
}
@NonNull @NonNull
public Object clone() throws CloneNotSupportedException { public Object clone() throws CloneNotSupportedException {

View file

@ -295,7 +295,7 @@ public class Account extends BaseAccount implements Serializable {
} }
try { try {
Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, null, null, null, null, null, null); Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, null, null, null, null, null, null);
return cursorToListUser(c); return cursorToListUserWithOwner(c);
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }

View file

@ -0,0 +1,62 @@
package app.fedilab.android.client.entities.app;
/* 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 <http://www.gnu.org/licenses>. */
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class Languages implements Serializable {
@SerializedName("languages")
public List<Language> languages;
public static List<Language> get(AppCompatActivity activity) {
try {
InputStream is = activity.getAssets().open("languages/iso_639_1.json");
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
String json = new String(buffer, StandardCharsets.UTF_8);
Gson gson = new Gson();
try {
return gson.fromJson(json, new TypeToken<List<Language>>() {
}.getType());
} catch (Exception e) {
return null;
}
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
public static class Language implements Serializable {
@SerializedName("code")
public String code;
@SerializedName("language")
public String language;
}
}

View file

@ -29,7 +29,6 @@ import app.fedilab.android.client.entities.api.Notification;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.sqlite.Sqlite; import app.fedilab.android.sqlite.Sqlite;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonNotification; import app.fedilab.android.ui.fragment.timeline.FragmentMastodonNotification;
@ -576,7 +575,6 @@ public class QuickLoad {
} }
quickLoad.position = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_POSITION)); quickLoad.position = c.getInt(c.getColumnIndexOrThrow(Sqlite.COL_POSITION));
//TimelineHelper.filterStatus(_mContext, quickLoad.statuses, TimelineHelper.FilterTimeLineType.PUBLIC); //TimelineHelper.filterStatus(_mContext, quickLoad.statuses, TimelineHelper.FilterTimeLineType.PUBLIC);
quickLoad.statuses = SpannableHelper.convertStatus(_mContext, quickLoad.statuses);
return quickLoad; return quickLoad;
} }

View file

@ -55,7 +55,7 @@ public class MisskeyNote implements Serializable {
@SerializedName("emojis") @SerializedName("emojis")
public List<MisskeyEmoji> emojis; public List<MisskeyEmoji> emojis;
public static Status convert(MisskeyNote misskeyNote) { public static Status convert(MisskeyNote misskeyNote, String instance) {
Status status = new Status(); Status status = new Status();
status.id = misskeyNote.id; status.id = misskeyNote.id;
status.in_reply_to_id = misskeyNote.replyId; status.in_reply_to_id = misskeyNote.replyId;
@ -64,7 +64,10 @@ public class MisskeyNote implements Serializable {
status.spoiler_text = misskeyNote.cw; status.spoiler_text = misskeyNote.cw;
status.visibility = misskeyNote.visibility; status.visibility = misskeyNote.visibility;
status.created_at = misskeyNote.createdAt; 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; status.url = misskeyNote.url;
Account account = new Account(); Account account = new Account();

View file

@ -105,8 +105,8 @@ public class Nitter implements Serializable {
} }
} }
Nitter nitterAccount = accounts.get(feedItem.creator); Nitter nitterAccount = accounts.get(feedItem.creator);
if (nitterAccount != null) {
app.fedilab.android.client.entities.api.Account account = new app.fedilab.android.client.entities.api.Account(); app.fedilab.android.client.entities.api.Account account = new app.fedilab.android.client.entities.api.Account();
if (nitterAccount != null) {
String[] names = nitterAccount.image.title.split("/"); String[] names = nitterAccount.image.title.split("/");
account.id = feedItem.guid; account.id = feedItem.guid;
account.acct = names[1].replace("@", ""); account.acct = names[1].replace("@", "");
@ -116,6 +116,15 @@ public class Nitter implements Serializable {
account.avatar_static = nitterAccount.image.url; account.avatar_static = nitterAccount.image.url;
account.url = nitterAccount.image.link; account.url = nitterAccount.image.link;
status.account = account; 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) { if (feedItem.description != null) {

View file

@ -149,7 +149,7 @@ public class CrossActionHelper {
} }
}); });
} else if (targetedStatus != null) { } 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status status = results.statuses.get(0); Status status = results.statuses.get(0);
@ -278,8 +278,6 @@ public class CrossActionHelper {
if (results != null) { if (results != null) {
if (results.statuses == null) { if (results.statuses == null) {
results.statuses = new ArrayList<>(); results.statuses = new ArrayList<>();
} else {
results.statuses = SpannableHelper.convertStatus(context, results.statuses);
} }
if (results.accounts == null) { if (results.accounts == null) {
results.accounts = new ArrayList<>(); results.accounts = new ArrayList<>();
@ -308,15 +306,15 @@ public class CrossActionHelper {
/** /**
* Fetch and federate the remote status * 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); MastodonSearchService mastodonSearchService = init(context, BaseMainActivity.currentInstance);
String search; String search;
if (targetedAccount.acct.contains("@")) { //Not from same instance if (targetedAcct.contains("@")) { //Not from same instance
search = targetedAccount.acct; search = targetedAcct;
} else { } else {
search = targetedAccount.acct + "@" + BaseMainActivity.currentInstance; search = targetedAcct + "@" + BaseMainActivity.currentInstance;
} }
new Thread(() -> { new Thread(() -> {
Call<Results> resultsCall = mastodonSearchService.search(ownerAccount.token, search, null, "accounts", false, true, false, 0, null, null, 1); Call<Results> 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 != null) {
if (results.statuses == null) { if (results.statuses == null) {
results.statuses = new ArrayList<>(); results.statuses = new ArrayList<>();
} else {
results.statuses = SpannableHelper.convertStatus(context, results.statuses);
} }
if (results.accounts == null) { if (results.accounts == null) {
results.accounts = new ArrayList<>(); results.accounts = new ArrayList<>();
@ -374,8 +370,6 @@ public class CrossActionHelper {
if (results != null) { if (results != null) {
if (results.statuses == null) { if (results.statuses == null) {
results.statuses = new ArrayList<>(); results.statuses = new ArrayList<>();
} else {
results.statuses = SpannableHelper.convertStatus(context, results.statuses);
} }
if (results.accounts == null) { if (results.accounts == null) {
results.accounts = new ArrayList<>(); results.accounts = new ArrayList<>();

View file

@ -0,0 +1,106 @@
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.Animatable;
import android.graphics.drawable.Drawable;
import android.text.style.ReplacementSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
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 app.fedilab.android.R;
public class CustomEmoji extends ReplacementSpan {
private final float scale;
private final WeakReference<View> viewWeakReference;
private Drawable imageDrawable;
CustomEmoji(WeakReference<View> 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);
}
@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);
}
@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<Drawable> getTarget(boolean animate) {
return new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> 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) {
}
};
}
}

View file

@ -87,8 +87,10 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide; 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.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.load.resource.gif.GifDrawable;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@ -119,7 +121,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -352,9 +354,9 @@ public class Helper {
public static int counter = 1; public static int counter = 1;
static { static {
Map<PatternType, Pattern> aMap = new HashMap<>(); LinkedHashMap<PatternType, Pattern> aMap = new LinkedHashMap<>();
aMap.put(PatternType.MENTION, mentionPattern);
aMap.put(PatternType.MENTION_LONG, mentionLongPattern); aMap.put(PatternType.MENTION_LONG, mentionLongPattern);
aMap.put(PatternType.MENTION, mentionPattern);
aMap.put(PatternType.TAG, hashtagPattern); aMap.put(PatternType.TAG, hashtagPattern);
aMap.put(PatternType.GROUP, groupPattern); aMap.put(PatternType.GROUP, groupPattern);
patternHashMap = Collections.unmodifiableMap(aMap); patternHashMap = Collections.unmodifiableMap(aMap);
@ -1043,6 +1045,7 @@ public class Helper {
} }
/** /**
* Load a profile picture for the account * Load a profile picture for the account
* *
@ -1050,25 +1053,39 @@ public class Helper {
* @param account - {@link Account} * @param account - {@link Account}
*/ */
public static void loadPP(ImageView view, BaseAccount 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(); Context context = view.getContext();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false); boolean disableGif = sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_GIF), false);
String targetedUrl = disableGif ? account.mastodon_account.avatar_static : account.mastodon_account.avatar; String targetedUrl = disableGif ? account.mastodon_account.avatar_static : account.mastodon_account.avatar;
if (targetedUrl != null) { if (targetedUrl != null) {
if (disableGif || (!targetedUrl.endsWith(".gif"))) { if (disableGif || (!targetedUrl.endsWith(".gif"))) {
Glide.with(view.getContext()) RequestBuilder<Drawable> requestBuilder = Glide.with(view.getContext())
.asDrawable() .asDrawable()
.load(targetedUrl) .load(targetedUrl)
.thumbnail(0.1f) .thumbnail(0.1f);
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) if (crop) {
.into(view); requestBuilder = requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)));
}
requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))).into(view);
} else { } else {
Glide.with(view.getContext()) RequestBuilder<GifDrawable> requestBuilder = Glide.with(view.getContext())
.asGif() .asGif()
.load(targetedUrl) .load(targetedUrl)
.thumbnail(0.1f) .thumbnail(0.1f);
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))) if (crop) {
.into(view); requestBuilder = requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10)));
}
requestBuilder.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners(10))).into(view);
} }
} else { } else {
Glide.with(view.getContext()) Glide.with(view.getContext())
@ -1722,4 +1739,13 @@ public class Helper {
}).start(); }).start();
} }
public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
for (Map.Entry<T, E> entry : map.entrySet()) {
if (Objects.equals(value, entry.getValue())) {
return entry.getKey();
}
}
return null;
}
} }

View file

@ -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;
}
}

View file

@ -115,11 +115,6 @@ public class NotificationsHelper {
if (notifications.notifications.size() > 0) { if (notifications.notifications.size() > 0) {
since_ids.put(slug, notifications.notifications.get(0).id); 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()); notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers());
} }

View file

@ -107,6 +107,18 @@ public class TimelineHelper {
Matcher m = p.matcher(content); Matcher m = p.matcher(content);
if (m.find()) { if (m.find()) {
statusesToRemove.add(status); 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 { } else {
@ -118,6 +130,18 @@ public class TimelineHelper {
content = Html.fromHtml(status.content).toString(); content = Html.fromHtml(status.content).toString();
if (content.contains(filter.phrase)) { if (content.contains(filter.phrase)) {
statusesToRemove.add(status); 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);
}
} }
} }
} }

View file

@ -75,6 +75,7 @@ public class NotificationsWorker extends Worker {
String instance = getInputData().getString(Helper.ARG_INSTANCE); String instance = getInputData().getString(Helper.ARG_INSTANCE);
String token = getInputData().getString(Helper.ARG_TOKEN); String token = getInputData().getString(Helper.ARG_TOKEN);
String statusDraftId = getInputData().getString(Helper.ARG_STATUS_DRAFT_ID); String statusDraftId = getInputData().getString(Helper.ARG_STATUS_DRAFT_ID);
String userId = getInputData().getString(Helper.ARG_USER_ID);
StatusDraft statusDraft; StatusDraft statusDraft;
try { try {
statusDraft = new StatusDraft(getApplicationContext()).geStatusDraft(statusDraftId); statusDraft = new StatusDraft(getApplicationContext()).geStatusDraft(statusDraftId);
@ -82,6 +83,7 @@ public class NotificationsWorker extends Worker {
intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft); intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft);
intent.putExtra(Helper.ARG_INSTANCE, instance); intent.putExtra(Helper.ARG_INSTANCE, instance);
intent.putExtra(Helper.ARG_TOKEN, token); intent.putExtra(Helper.ARG_TOKEN, token);
intent.putExtra(Helper.ARG_USER_ID, userId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getApplicationContext().startForegroundService(intent); getApplicationContext().startForegroundService(intent);
} else { } else {

View file

@ -74,6 +74,7 @@ public class ScheduleThreadWorker extends Worker {
Data outputData; Data outputData;
String instance = getInputData().getString(Helper.ARG_INSTANCE); String instance = getInputData().getString(Helper.ARG_INSTANCE);
String token = getInputData().getString(Helper.ARG_TOKEN); String token = getInputData().getString(Helper.ARG_TOKEN);
String userId = getInputData().getString(Helper.ARG_USER_ID);
String statusDraftId = getInputData().getString(Helper.ARG_STATUS_DRAFT_ID); String statusDraftId = getInputData().getString(Helper.ARG_STATUS_DRAFT_ID);
StatusDraft statusDraft; StatusDraft statusDraft;
try { try {
@ -82,6 +83,7 @@ public class ScheduleThreadWorker extends Worker {
intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft); intent.putExtra(Helper.ARG_STATUS_DRAFT, statusDraft);
intent.putExtra(Helper.ARG_INSTANCE, instance); intent.putExtra(Helper.ARG_INSTANCE, instance);
intent.putExtra(Helper.ARG_TOKEN, token); intent.putExtra(Helper.ARG_TOKEN, token);
intent.putExtra(Helper.ARG_USER_ID, userId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
getApplicationContext().startForegroundService(intent); getApplicationContext().startForegroundService(intent);
} else { } else {

View file

@ -202,9 +202,10 @@ public class PostMessageService extends IntentService {
if (error) { if (error) {
return; return;
} }
String language = sharedPreferences.getString(context.getString(R.string.SET_COMPOSE_LANGUAGE) + dataPost.userId + dataPost.instance, null);
if (dataPost.scheduledDate == null) { if (dataPost.scheduledDate == null) {
statusCall = mastodonStatusesService.createStatus(null, dataPost.token, statuses.get(i).text, attachmentIds, poll_options, poll_expire_in, statusCall = mastodonStatusesService.createStatus(null, dataPost.token, statuses.get(i).text, attachmentIds, poll_options, poll_expire_in,
poll_multiple, poll_hide_totals, in_reply_to_status, statuses.get(i).sensitive, statuses.get(i).spoiler_text, statuses.get(i).visibility.toLowerCase(), statuses.get(i).language); poll_multiple, poll_hide_totals, in_reply_to_status, statuses.get(i).sensitive, statuses.get(i).spoiler_text, statuses.get(i).visibility.toLowerCase(), language);
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
@ -339,11 +340,13 @@ public class PostMessageService extends IntentService {
StatusDraft statusDraft = null; StatusDraft statusDraft = null;
String token = null, instance = null; String token = null, instance = null;
String scheduledDate = null; String scheduledDate = null;
String userId = null;
if (intent != null && intent.getExtras() != null) { if (intent != null && intent.getExtras() != null) {
Bundle b = intent.getExtras(); Bundle b = intent.getExtras();
statusDraft = (StatusDraft) b.getSerializable(Helper.ARG_STATUS_DRAFT); statusDraft = (StatusDraft) b.getSerializable(Helper.ARG_STATUS_DRAFT);
token = b.getString(Helper.ARG_TOKEN); token = b.getString(Helper.ARG_TOKEN);
instance = b.getString(Helper.ARG_INSTANCE); instance = b.getString(Helper.ARG_INSTANCE);
userId = b.getString(Helper.ARG_USER_ID);
scheduledDate = b.getString(Helper.ARG_SCHEDULED_DATE); scheduledDate = b.getString(Helper.ARG_SCHEDULED_DATE);
} }
//Should not be null, but a simple security //Should not be null, but a simple security
@ -356,6 +359,7 @@ public class PostMessageService extends IntentService {
DataPost dataPost = new DataPost(); DataPost dataPost = new DataPost();
dataPost.instance = instance; dataPost.instance = instance;
dataPost.token = token; dataPost.token = token;
dataPost.userId = userId;
dataPost.statusDraft = statusDraft; dataPost.statusDraft = statusDraft;
dataPost.scheduledDate = scheduledDate; dataPost.scheduledDate = scheduledDate;
dataPost.notificationBuilder = notificationBuilder; dataPost.notificationBuilder = notificationBuilder;
@ -367,6 +371,7 @@ public class PostMessageService extends IntentService {
static class DataPost { static class DataPost {
String instance; String instance;
String token; String token;
String userId;
StatusDraft statusDraft; StatusDraft statusDraft;
int messageToSend; int messageToSend;
int messageSent; int messageSent;

View file

@ -22,9 +22,10 @@ import app.fedilab.android.client.entities.app.StatusDraft;
public class ThreadMessageService { public class ThreadMessageService {
public ThreadMessageService(Context context, String instance, String token, StatusDraft statusDraft, String scheduledDate) { public ThreadMessageService(Context context, String instance, String userId, String token, StatusDraft statusDraft, String scheduledDate) {
PostMessageService.DataPost dataPost = new PostMessageService.DataPost(); PostMessageService.DataPost dataPost = new PostMessageService.DataPost();
dataPost.instance = instance; dataPost.instance = instance;
dataPost.userId = userId;
dataPost.token = token; dataPost.token = token;
dataPost.scheduledDate = scheduledDate; dataPost.scheduledDate = scheduledDate;
dataPost.statusDraft = statusDraft; dataPost.statusDraft = statusDraft;

View file

@ -37,6 +37,7 @@ import androidx.lifecycle.ViewModelStoreOwner;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.lang.ref.WeakReference;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -224,9 +225,15 @@ public class AccountAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
} }
}); });
} }
accountViewHolder.binding.displayName.setText(account.span_display_name, TextView.BufferType.SPANNABLE); accountViewHolder.binding.displayName.setText(
account.getSpanDisplayName(context,
new WeakReference<>(accountViewHolder.binding.displayName)),
TextView.BufferType.SPANNABLE);
accountViewHolder.binding.username.setText(String.format("@%s", account.acct)); accountViewHolder.binding.username.setText(String.format("@%s", account.acct));
accountViewHolder.binding.bio.setText(account.span_note, TextView.BufferType.SPANNABLE); accountViewHolder.binding.bio.setText(
account.getSpanNote(context,
new WeakReference<>(accountViewHolder.binding.bio)),
TextView.BufferType.SPANNABLE);
} }
public int getCount() { public int getCount() {

View file

@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -78,7 +79,10 @@ public class AccountListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
account = getItem(position); account = getItem(position);
AccountListViewHolder holder = (AccountListViewHolder) viewHolder; AccountListViewHolder holder = (AccountListViewHolder) viewHolder;
MastodonHelper.loadPPMastodon(holder.binding.avatar, account); MastodonHelper.loadPPMastodon(holder.binding.avatar, account);
holder.binding.displayName.setText(account.span_display_name, TextView.BufferType.SPANNABLE); holder.binding.displayName.setText(
account.getSpanDisplayName(context,
new WeakReference<>(holder.binding.displayName)),
TextView.BufferType.SPANNABLE);
holder.binding.username.setText(String.format("@%s", account.acct)); holder.binding.username.setText(String.format("@%s", account.acct));
if (searchList != null) { if (searchList != null) {

View file

@ -37,6 +37,7 @@ import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.EmojiPopup; import com.vanniktech.emoji.EmojiPopup;
import com.vanniktech.emoji.one.EmojiOneProvider; import com.vanniktech.emoji.one.EmojiOneProvider;
import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
@ -75,19 +76,23 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
return new AnnouncementHolder(itemBinding); return new AnnouncementHolder(itemBinding);
} }
@Override @Override
public void onBindViewHolder(@NonNull AnnouncementHolder holder, int position) { public void onBindViewHolder(@NonNull AnnouncementHolder holder, int position) {
Announcement announcement = announcements.get(position); Announcement announcement = announcements.get(position);
if (announcement.reactions != null && announcement.reactions.size() > 0) { if (announcement.reactions != null && announcement.reactions.size() > 0) {
ReactionAdapter reactionAdapter = new ReactionAdapter(announcement.id, announcement.reactions); ReactionAdapter reactionAdapter = new ReactionAdapter(announcement.id, announcement.reactions);
holder.binding.reactionsView.setAdapter(reactionAdapter); holder.binding.layoutReactions.reactionsView.setAdapter(reactionAdapter);
LinearLayoutManager layoutManager LinearLayoutManager layoutManager
= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
holder.binding.reactionsView.setLayoutManager(layoutManager); holder.binding.layoutReactions.reactionsView.setLayoutManager(layoutManager);
} else { } else {
holder.binding.reactionsView.setAdapter(null); holder.binding.layoutReactions.reactionsView.setAdapter(null);
} }
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) { if (announcement.starts_at != null) {
String dateIni; String dateIni;
String dateEnd; String dateEnd;
@ -104,11 +109,11 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
} else { } else {
holder.binding.dates.setVisibility(View.GONE); holder.binding.dates.setVisibility(View.GONE);
} }
holder.binding.statusEmoji.setOnClickListener(v -> { holder.binding.layoutReactions.statusEmoji.setOnClickListener(v -> {
EmojiManager.install(new EmojiOneProvider()); EmojiManager.install(new EmojiOneProvider());
final EmojiPopup emojiPopup = EmojiPopup.Builder.fromRootView(holder.binding.statusEmoji).setOnEmojiPopupDismissListener(() -> { final EmojiPopup emojiPopup = EmojiPopup.Builder.fromRootView(holder.binding.layoutReactions.statusEmoji).setOnEmojiPopupDismissListener(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(holder.binding.statusEmoji.getWindowToken(), 0); imm.hideSoftInputFromWindow(holder.binding.layoutReactions.statusEmoji.getWindowToken(), 0);
}).setOnEmojiClickListener((emoji, imageView) -> { }).setOnEmojiClickListener((emoji, imageView) -> {
String emojiStr = imageView.getUnicode(); String emojiStr = imageView.getUnicode();
boolean alreadyAdded = false; boolean alreadyAdded = false;
@ -138,10 +143,10 @@ public class AnnouncementAdapter extends RecyclerView.Adapter<AnnouncementAdapte
announcementsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcement.id, emojiStr); announcementsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, announcement.id, emojiStr);
} }
}) })
.build(holder.binding.fakeEdittext); .build(holder.binding.layoutReactions.fakeEdittext);
emojiPopup.toggle(); emojiPopup.toggle();
}); });
holder.binding.statusAddCustomEmoji.setOnClickListener(v -> { holder.binding.layoutReactions.statusAddCustomEmoji.setOnClickListener(v -> {
final AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle()); final AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
int paddingPixel = 15; int paddingPixel = 15;
float density = context.getResources().getDisplayMetrics().density; float density = context.getResources().getDisplayMetrics().density;

View file

@ -73,6 +73,7 @@ import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference;
import java.text.Normalizer; import java.text.Normalizer;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -455,13 +456,18 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
List<Attachment> attachmentList = statusList.get(position).media_attachments; List<Attachment> attachmentList = statusList.get(position).media_attachments;
if (attachmentList != null && attachmentList.size() > 0) { if (attachmentList != null && attachmentList.size() > 0) {
holder.binding.sensitiveMedia.setVisibility(View.VISIBLE); holder.binding.sensitiveMedia.setVisibility(View.VISIBLE);
if (!statusList.get(position).sensitive) {
if (currentAccount.mastodon_account.source != null) { if (currentAccount.mastodon_account.source != null) {
holder.binding.sensitiveMedia.setChecked(currentAccount.mastodon_account.source.sensitive); holder.binding.sensitiveMedia.setChecked(currentAccount.mastodon_account.source.sensitive);
statusList.get(position).sensitive = currentAccount.mastodon_account.source.sensitive; statusList.get(position).sensitive = currentAccount.mastodon_account.source.sensitive;
} else { } else {
statusList.get(position).sensitive = false; 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; int mediaPosition = 0;
for (Attachment attachment : attachmentList) { for (Attachment attachment : attachmentList) {
ComposeAttachmentItemBinding composeAttachmentItemBinding = ComposeAttachmentItemBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false); ComposeAttachmentItemBinding composeAttachmentItemBinding = ComposeAttachmentItemBinding.inflate(LayoutInflater.from(context), holder.binding.attachmentsList, false);
@ -664,6 +670,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
return statusList.size(); return statusList.size();
} }
private List<Emoji> emojisList = new ArrayList<>();
/** /**
* Initialize text watcher for content writing * Initialize text watcher for content writing
* It will allow to complete autocomplete edit text while starting words with @, #, : etc. * It will allow to complete autocomplete edit text while starting words with @, #, : etc.
@ -672,7 +679,6 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
* @return {@link TextWatcher} * @return {@link TextWatcher}
*/ */
public TextWatcher initializeTextWatcher(ComposeAdapter.ComposeViewHolder holder) { public TextWatcher initializeTextWatcher(ComposeAdapter.ComposeViewHolder holder) {
final List<Emoji>[] emojis = new List[]{null};
String pattern = "(.|\\s)*(@[\\w_-]+@[a-z0-9.\\-]+|@[\\w_-]+)"; String pattern = "(.|\\s)*(@[\\w_-]+@[a-z0-9.\\-]+|@[\\w_-]+)";
final Pattern mentionPattern = Pattern.compile(pattern); final Pattern mentionPattern = Pattern.compile(pattern);
@ -958,13 +964,13 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
new Thread(() -> { new Thread(() -> {
List<Emoji> emojisToDisplay = new ArrayList<>(); List<Emoji> emojisToDisplay = new ArrayList<>();
try { try {
if (emojis[0] == null) { if (emojisList == null || emojisList.size() == 0) {
emojis[0] = new EmojiInstance(context).getEmojiList(BaseMainActivity.currentInstance); emojisList = new EmojiInstance(context).getEmojiList(BaseMainActivity.currentInstance);
} }
if (emojis[0] == null) { if (emojis == null) {
return; return;
} }
for (Emoji emoji : emojis[0]) { for (Emoji emoji : emojisList) {
if (shortcode != null && emoji.shortcode.contains(shortcode)) { if (shortcode != null && emoji.shortcode.contains(shortcode)) {
emojisToDisplay.add(emoji); emojisToDisplay.add(emoji);
if (emojisToDisplay.size() >= 10) { if (emojisToDisplay.size() >= 10) {
@ -983,7 +989,7 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
if (searchA.length > 0) { if (searchA.length > 0) {
final String search = searchA[searchA.length - 1]; final String search = searchA[searchA.length - 1];
holder.binding.content.setOnItemClickListener((parent, view, position, id) -> { holder.binding.content.setOnItemClickListener((parent, view, position, id) -> {
String shortcodeSelected = emojis[0].get(position).shortcode; String shortcodeSelected = emojisToDisplay.get(position).shortcode;
String deltaSearch = ""; String deltaSearch = "";
int searchLength = searchDeep; int searchLength = searchDeep;
if (currentCursorPosition < searchDeep) { //Less than 15 characters are written before the cursor position if (currentCursorPosition < searchDeep) { //Less than 15 characters are written before the cursor position
@ -1032,21 +1038,40 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@Override @Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
int theme_statuses_color = -1;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (sharedpreferences.getBoolean("use_custom_theme", false)) {
theme_statuses_color = sharedpreferences.getInt("theme_statuses_color", -1);
}
if (getItemViewType(position) == TYPE_NORMAL) { if (getItemViewType(position) == TYPE_NORMAL) {
Status status = statusList.get(position); Status status = statusList.get(position);
StatusSimpleViewHolder holder = (StatusSimpleViewHolder) viewHolder; StatusSimpleViewHolder holder = (StatusSimpleViewHolder) viewHolder;
holder.binding.statusContent.setText(status.span_content, TextView.BufferType.SPANNABLE); holder.binding.statusContent.setText(
status.getSpanContent(context,
new WeakReference<>(holder.binding.statusContent)),
TextView.BufferType.SPANNABLE);
MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account); MastodonHelper.loadPPMastodon(holder.binding.avatar, status.account);
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)),
TextView.BufferType.SPANNABLE);
holder.binding.username.setText(String.format("@%s", status.account.acct)); holder.binding.username.setText(String.format("@%s", status.account.acct));
if (status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) { if (status.spoiler_text != null && !status.spoiler_text.trim().isEmpty()) {
holder.binding.spoiler.setVisibility(View.VISIBLE); 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)),
TextView.BufferType.SPANNABLE);
} else { } else {
holder.binding.spoiler.setVisibility(View.GONE); holder.binding.spoiler.setVisibility(View.GONE);
holder.binding.spoiler.setText(null); holder.binding.spoiler.setText(null);
} }
if (theme_statuses_color != -1) {
holder.binding.cardviewContainer.setBackgroundColor(theme_statuses_color);
} else {
holder.binding.cardviewContainer.setBackgroundColor(ContextCompat.getColor(context, R.color.cyanea_primary_dark_reference));
}
} else if (getItemViewType(position) == TYPE_COMPOSE) { } else if (getItemViewType(position) == TYPE_COMPOSE) {
Status statusDraft = statusList.get(position); Status statusDraft = statusList.get(position);
@ -1058,7 +1083,11 @@ public class ComposeAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
int newInputTypeSpoiler = holder.binding.contentSpoiler.getInputType() & (holder.binding.contentSpoiler.getInputType() ^ InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); int newInputTypeSpoiler = holder.binding.contentSpoiler.getInputType() & (holder.binding.contentSpoiler.getInputType() ^ InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
holder.binding.contentSpoiler.setInputType(newInputTypeSpoiler); holder.binding.contentSpoiler.setInputType(newInputTypeSpoiler);
if (theme_statuses_color != -1) {
holder.binding.cardviewContainer.setBackgroundColor(theme_statuses_color);
} else {
holder.binding.cardviewContainer.setBackgroundColor(ContextCompat.getColor(context, R.color.cyanea_primary_dark_reference));
}
holder.binding.buttonAttach.setOnClickListener(v -> { holder.binding.buttonAttach.setOnClickListener(v -> {
if (instanceInfo.configuration.media_attachments.supported_mime_types != null) { if (instanceInfo.configuration.media_attachments.supported_mime_types != null) {
if (instanceInfo.getMimeTypeAudio().size() == 0) { if (instanceInfo.getMimeTypeAudio().size() == 0) {

View file

@ -36,6 +36,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer; import java.util.Timer;
@ -137,14 +138,20 @@ public class ConversationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
notifyItemChanged(position); notifyItemChanged(position);
}); });
holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE);
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)),
TextView.BufferType.SPANNABLE);
} else { } else {
holder.binding.spoiler.setVisibility(View.GONE); holder.binding.spoiler.setVisibility(View.GONE);
holder.binding.spoilerExpand.setVisibility(View.GONE); holder.binding.spoilerExpand.setVisibility(View.GONE);
holder.binding.spoiler.setText(null); holder.binding.spoiler.setText(null);
} }
//--- MAIN CONTENT --- //--- MAIN CONTENT ---
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)),
TextView.BufferType.SPANNABLE);
//--- DATE --- //--- DATE ---
holder.binding.lastMessageDate.setText(Helper.dateDiff(context, conversation.last_status.created_at)); holder.binding.lastMessageDate.setText(Helper.dateDiff(context, conversation.last_status.created_at));

View file

@ -16,26 +16,16 @@ package app.fedilab.android.ui.drawer;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; 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 java.util.List;
import app.fedilab.android.R; import app.fedilab.android.R;
@ -52,7 +42,7 @@ public class EmojiAdapter extends BaseAdapter {
} }
public int getCount() { public int getCount() {
return emojiList.size(); return emojiList == null ? 0 : emojiList.size();
} }
public Emoji getItem(int position) { public Emoji getItem(int position) {
@ -76,28 +66,8 @@ public class EmojiAdapter extends BaseAdapter {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(holder.view.getContext()); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(holder.view.getContext());
boolean disableAnimatedEmoji = sharedpreferences.getBoolean(parent.getContext().getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); boolean disableAnimatedEmoji = sharedpreferences.getBoolean(parent.getContext().getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false);
Glide.with(holder.binding.imgCustomEmoji.getContext()) Glide.with(holder.binding.imgCustomEmoji.getContext())
.asFile()
.load(!disableAnimatedEmoji ? emoji.url : emoji.static_url) .load(!disableAnimatedEmoji ? emoji.url : emoji.static_url)
.into(new CustomTarget<File>() { .into(holder.binding.imgCustomEmoji);
@Override
public void onResourceReady(@NonNull File resource, @Nullable Transition<? super File> 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) {
}
});
return holder.view; return holder.view;
} }

View file

@ -16,9 +16,7 @@ package app.fedilab.android.ui.drawer;
import android.content.Context; import android.content.Context;
import android.text.Spannable;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
@ -27,9 +25,11 @@ import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import app.fedilab.android.R; 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.client.entities.api.Field;
import app.fedilab.android.databinding.DrawerFieldBinding; import app.fedilab.android.databinding.DrawerFieldBinding;
@ -38,6 +38,7 @@ public class FieldAdapter extends RecyclerView.Adapter<FieldAdapter.FieldViewHol
private final List<Field> fields; private final List<Field> fields;
private Context context; private Context context;
private Account account;
public FieldAdapter(List<Field> fields) { public FieldAdapter(List<Field> fields) {
this.fields = fields; this.fields = fields;
@ -66,9 +67,11 @@ public class FieldAdapter extends RecyclerView.Adapter<FieldAdapter.FieldViewHol
Field field = fields.get(position); Field field = fields.get(position);
if (field.verified_at != null) { if (field.verified_at != null) {
holder.binding.value.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(context, R.drawable.ic_baseline_verified_24), null); holder.binding.value.setCompoundDrawablesWithIntrinsicBounds(null, null, ContextCompat.getDrawable(context, R.drawable.ic_baseline_verified_24), null);
field.value_span.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, R.color.verified_text)), 0, field.value_span.toString().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
holder.binding.value.setText(field.value_span != null ? field.value_span : field.value, TextView.BufferType.SPANNABLE); holder.binding.value.setText(
field.getValueSpan(context, account,
new WeakReference<>(holder.binding.value)),
TextView.BufferType.SPANNABLE);
holder.binding.value.setMovementMethod(LinkMovementMethod.getInstance()); holder.binding.value.setMovementMethod(LinkMovementMethod.getInstance());
holder.binding.label.setText(field.name); holder.binding.label.setText(field.name);
} }

View file

@ -24,6 +24,7 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
@ -31,6 +32,7 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import java.lang.ref.WeakReference;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -58,6 +60,7 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
private final int TYPE_POLL = 5; private final int TYPE_POLL = 5;
private final int TYPE_STATUS = 6; private final int TYPE_STATUS = 6;
private final int NOTIFICATION_FETCH_MORE = 7; private final int NOTIFICATION_FETCH_MORE = 7;
private final int TYPE_REACTION = 8;
public FetchMoreCallBack fetchMoreCallBack; public FetchMoreCallBack fetchMoreCallBack;
private Context context; private Context context;
@ -94,6 +97,8 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
return TYPE_POLL; return TYPE_POLL;
case "status": case "status":
return TYPE_STATUS; return TYPE_STATUS;
case "pleroma:emoji_reaction":
return TYPE_REACTION;
} }
return super.getItemViewType(position); return super.getItemViewType(position);
} }
@ -114,13 +119,36 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
} }
} }
/**
* Will manage the current position of the element in the adapter. Action is async, and position might have changed
*
* @param notificationList List<Notification> - Not null when calling from notification adapter
* @param id String - Current status
* @return int - position in real time
*/
public static int getPositionAsync(List<Notification> 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 @Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
Notification notification = notificationList.get(position); Notification notification = notificationList.get(position);
if (getItemViewType(position) == TYPE_FOLLOW || getItemViewType(position) == TYPE_FOLLOW_REQUEST) { if (getItemViewType(position) == TYPE_FOLLOW || getItemViewType(position) == TYPE_FOLLOW_REQUEST) {
ViewHolderFollow holderFollow = (ViewHolderFollow) viewHolder; ViewHolderFollow holderFollow = (ViewHolderFollow) viewHolder;
MastodonHelper.loadPPMastodon(holderFollow.binding.avatar, notification.account); MastodonHelper.loadPPMastodon(holderFollow.binding.avatar, notification.account);
holderFollow.binding.displayName.setText(notification.account.display_name); holderFollow.binding.displayName.setText(
notification.account.getSpanDisplayName(context,
new WeakReference<>(holderFollow.binding.displayName)),
TextView.BufferType.SPANNABLE);
holderFollow.binding.username.setText(String.format("@%s", notification.account.acct)); holderFollow.binding.username.setText(String.format("@%s", notification.account.acct));
if (getItemViewType(position) == TYPE_FOLLOW_REQUEST) { if (getItemViewType(position) == TYPE_FOLLOW_REQUEST) {
holderFollow.binding.rejectButton.setVisibility(View.VISIBLE); holderFollow.binding.rejectButton.setVisibility(View.VISIBLE);
@ -172,6 +200,8 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_star_24); holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_star_24);
} else if (getItemViewType(position) == TYPE_REBLOG) { } else if (getItemViewType(position) == TYPE_REBLOG) {
holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_repeat_24); holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_repeat_24);
} else if (getItemViewType(position) == TYPE_REACTION) {
holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_insert_emoticon_24);
} else if (getItemViewType(position) == TYPE_POLL) { } else if (getItemViewType(position) == TYPE_POLL) {
holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_poll_24); holderStatus.bindingNotification.status.typeOfNotification.setImageResource(R.drawable.ic_baseline_poll_24);
} }
@ -180,15 +210,25 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
statusManagement(context, statusesVM, searchVM, holderStatus, this, null, notificationList, notification.status, Timeline.TimeLineEnum.NOTIFICATION, false, true); statusManagement(context, statusesVM, searchVM, holderStatus, this, null, notificationList, notification.status, Timeline.TimeLineEnum.NOTIFICATION, false, true);
holderStatus.bindingNotification.status.dateShort.setText(Helper.dateDiff(context, notification.created_at)); holderStatus.bindingNotification.status.dateShort.setText(Helper.dateDiff(context, notification.created_at));
holderStatus.bindingNotification.containerTransparent.setAlpha(.3f); holderStatus.bindingNotification.containerTransparent.setAlpha(.3f);
if (getItemViewType(position) == TYPE_MENTION || getItemViewType(position) == TYPE_STATUS) { if (getItemViewType(position) == TYPE_MENTION || getItemViewType(position) == TYPE_STATUS || getItemViewType(position) == TYPE_REACTION) {
holderStatus.bindingNotification.status.actionButtons.setVisibility(View.VISIBLE); holderStatus.bindingNotification.status.actionButtons.setVisibility(View.VISIBLE);
String title = ""; String title = "";
if (getItemViewType(position) == TYPE_MENTION) { if (getItemViewType(position) == TYPE_MENTION) {
title = String.format(Locale.getDefault(), "%s %s", notification.account.display_name, context.getString(R.string.notif_mention)); title = String.format(Locale.getDefault(), "%s %s", notification.account.display_name, context.getString(R.string.notif_mention));
} else if (getItemViewType(position) == TYPE_STATUS) { } else if (getItemViewType(position) == TYPE_STATUS) {
title = String.format(Locale.getDefault(), "%s %s", notification.account.display_name, context.getString(R.string.notif_status)); title = String.format(Locale.getDefault(), "%s %s", notification.account.display_name, context.getString(R.string.notif_status));
} else if (getItemViewType(position) == TYPE_REACTION) {
if (notification.emoji == null) {
notification.emoji = "";
} }
holderStatus.bindingNotification.status.displayName.setText(title); title = String.format(Locale.getDefault(), "%s reacted with %s", notification.account.username, notification.emoji);
MastodonHelper.loadPPMastodon(holderStatus.bindingNotification.status.avatar, notification.account);
}
notification.account.display_name = title;
holderStatus.bindingNotification.status.displayName.setText(
notification.account.getSpanDisplayName(context,
new WeakReference<>(holderStatus.bindingNotification.status.displayName)),
TextView.BufferType.SPANNABLE);
holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct)); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct));
holderStatus.bindingNotification.containerTransparent.setAlpha(.1f); holderStatus.bindingNotification.containerTransparent.setAlpha(.1f);
if (notification.status != null && notification.status.visibility.equalsIgnoreCase("direct")) { if (notification.status != null && notification.status.visibility.equalsIgnoreCase("direct")) {
@ -255,13 +295,19 @@ public class NotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
// start the new activity // start the new activity
context.startActivity(intent, options.toBundle()); context.startActivity(intent, options.toBundle());
}); });
holderStatus.bindingNotification.status.displayName.setText(title); notification.account.display_name = title;
holderStatus.bindingNotification.status.displayName.setText(
notification.account.getSpanDisplayName(context,
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)); holderStatus.bindingNotification.status.username.setText(String.format("@%s", notification.account.acct));
holderStatus.bindingNotification.status.actionButtons.setVisibility(View.GONE); holderStatus.bindingNotification.status.actionButtons.setVisibility(View.GONE);
} }
} }
} }
public long getItemId(int position) { public long getItemId(int position) {
return position; return position;
} }

View file

@ -15,6 +15,8 @@ package app.fedilab.android.ui.drawer;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static android.content.Context.INPUT_METHOD_SERVICE;
import static app.fedilab.android.BaseMainActivity.emojis;
import static app.fedilab.android.BaseMainActivity.regex_home; import static app.fedilab.android.BaseMainActivity.regex_home;
import static app.fedilab.android.BaseMainActivity.regex_local; import static app.fedilab.android.BaseMainActivity.regex_local;
import static app.fedilab.android.BaseMainActivity.regex_public; import static app.fedilab.android.BaseMainActivity.regex_public;
@ -35,10 +37,7 @@ import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Looper;
import android.text.Html; import android.text.Html;
import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
@ -49,7 +48,9 @@ import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.GridView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.RadioButton; import android.widget.RadioButton;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
@ -69,9 +70,11 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; 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.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
@ -80,14 +83,16 @@ import com.github.stom79.mytransl.client.HttpsConnectionException;
import com.github.stom79.mytransl.client.Results; import com.github.stom79.mytransl.client.Results;
import com.github.stom79.mytransl.translate.Params; import com.github.stom79.mytransl.translate.Params;
import com.github.stom79.mytransl.translate.Translate; import com.github.stom79.mytransl.translate.Translate;
import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.EmojiPopup;
import com.vanniktech.emoji.one.EmojiOneProvider;
import com.varunest.sparkbutton.SparkButton; import com.varunest.sparkbutton.SparkButton;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -96,6 +101,7 @@ import app.fedilab.android.R;
import app.fedilab.android.activities.ComposeActivity; import app.fedilab.android.activities.ComposeActivity;
import app.fedilab.android.activities.ContextActivity; import app.fedilab.android.activities.ContextActivity;
import app.fedilab.android.activities.CustomSharingActivity; import app.fedilab.android.activities.CustomSharingActivity;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.activities.MediaActivity; import app.fedilab.android.activities.MediaActivity;
import app.fedilab.android.activities.ProfileActivity; import app.fedilab.android.activities.ProfileActivity;
import app.fedilab.android.activities.ReportActivity; import app.fedilab.android.activities.ReportActivity;
@ -103,7 +109,9 @@ import app.fedilab.android.activities.StatusInfoActivity;
import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Notification; import app.fedilab.android.client.entities.api.Notification;
import app.fedilab.android.client.entities.api.Poll; import app.fedilab.android.client.entities.api.Poll;
import app.fedilab.android.client.entities.api.Reaction;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.client.entities.app.StatusDraft; import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.client.entities.app.Timeline;
@ -127,6 +135,7 @@ import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.viewmodel.mastodon.SearchVM; import app.fedilab.android.viewmodel.mastodon.SearchVM;
import app.fedilab.android.viewmodel.mastodon.StatusesVM; import app.fedilab.android.viewmodel.mastodon.StatusesVM;
import app.fedilab.android.viewmodel.pleroma.ActionsVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import jp.wasabeef.glide.transformations.BlurTransformation; import jp.wasabeef.glide.transformations.BlurTransformation;
@ -253,6 +262,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
statusToDeal.bookmarked = statusReturned.bookmarked; statusToDeal.bookmarked = statusReturned.bookmarked;
statusToDeal.reblogs_count = statusReturned.reblogs_count; statusToDeal.reblogs_count = statusReturned.reblogs_count;
statusToDeal.favourites_count = statusReturned.favourites_count; statusToDeal.favourites_count = statusReturned.favourites_count;
//Update status in cache if not a remote instance //Update status in cache if not a remote instance
if (!remote) { if (!remote) {
new Thread(() -> { new Thread(() -> {
@ -315,10 +325,120 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
boolean fullAttachement = sharedpreferences.getBoolean(context.getString(R.string.SET_FULL_PREVIEW), false); boolean fullAttachement = sharedpreferences.getBoolean(context.getString(R.string.SET_FULL_PREVIEW), false);
boolean displayBookmark = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_BOOKMARK), false); boolean displayBookmark = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_BOOKMARK), false);
if (MainActivity.currentAccount != null && MainActivity.currentAccount.api == Account.API.PLEROMA) {
holder.binding.layoutReactions.getRoot().setVisibility(View.VISIBLE);
if (status.pleroma != null && status.pleroma.emoji_reactions != null && status.pleroma.emoji_reactions.size() > 0) {
ReactionAdapter reactionAdapter = new ReactionAdapter(status.id, status.pleroma.emoji_reactions);
holder.binding.layoutReactions.reactionsView.setAdapter(reactionAdapter);
LinearLayoutManager layoutManager
= new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
holder.binding.layoutReactions.reactionsView.setLayoutManager(layoutManager);
} else {
holder.binding.layoutReactions.reactionsView.setAdapter(null);
}
holder.binding.layoutReactions.statusEmoji.setOnClickListener(v -> {
EmojiManager.install(new EmojiOneProvider());
final EmojiPopup emojiPopup = EmojiPopup.Builder.fromRootView(holder.binding.layoutReactions.statusEmoji).setOnEmojiPopupDismissListener(() -> {
InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(holder.binding.layoutReactions.statusEmoji.getWindowToken(), 0);
}).setOnEmojiClickListener((emoji, imageView) -> {
String emojiStr = imageView.getUnicode();
boolean alreadyAdded = false;
for (Reaction reaction : status.pleroma.emoji_reactions) {
if (reaction.name.compareTo(emojiStr) == 0) {
alreadyAdded = true;
reaction.count = (reaction.count - 1);
if (reaction.count == 0) {
status.pleroma.emoji_reactions.remove(reaction);
}
adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
break;
}
}
if (!alreadyAdded) {
Reaction reaction = new Reaction();
reaction.me = true;
reaction.count = 1;
reaction.name = emojiStr;
status.pleroma.emoji_reactions.add(0, reaction);
adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
}
ActionsVM actionVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
if (alreadyAdded) {
actionVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
} else {
actionVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
}
})
.build(holder.binding.layoutReactions.fakeEdittext);
emojiPopup.toggle();
});
holder.binding.layoutReactions.statusAddCustomEmoji.setOnClickListener(v -> {
final AlertDialog.Builder builder = new AlertDialog.Builder(context, Helper.dialogStyle());
int paddingPixel = 15;
float density = context.getResources().getDisplayMetrics().density;
int paddingDp = (int) (paddingPixel * density);
builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builder.setTitle(R.string.insert_emoji);
AlertDialog alertDialogEmoji = null;
if (emojis != null && emojis.size() > 0 && emojis.get(BaseMainActivity.currentInstance) != null) {
GridView gridView = new GridView(context);
gridView.setAdapter(new EmojiAdapter(emojis.get(BaseMainActivity.currentInstance)));
gridView.setNumColumns(5);
AlertDialog finalAlertDialogEmoji = alertDialogEmoji;
gridView.setOnItemClickListener((parent, view, index, id) -> {
String emojiStr = emojis.get(BaseMainActivity.currentInstance).get(index).shortcode;
String url = emojis.get(BaseMainActivity.currentInstance).get(index).url;
String static_url = emojis.get(BaseMainActivity.currentInstance).get(index).static_url;
boolean alreadyAdded = false;
for (Reaction reaction : status.pleroma.emoji_reactions) {
if (reaction.name.compareTo(emojiStr) == 0) {
alreadyAdded = true;
reaction.count = (reaction.count - 1);
if (reaction.count == 0) {
status.pleroma.emoji_reactions.remove(reaction);
}
adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
break;
}
}
if (!alreadyAdded) {
Reaction reaction = new Reaction();
reaction.me = true;
reaction.count = 1;
reaction.name = emojiStr;
reaction.url = url;
reaction.static_url = static_url;
status.pleroma.emoji_reactions.add(0, reaction);
adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
}
ActionsVM actionsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(ActionsVM.class);
if (alreadyAdded) {
actionsVM.removeReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
} else {
actionsVM.addReaction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, emojiStr);
}
if (finalAlertDialogEmoji != null) {
finalAlertDialogEmoji.dismiss();
}
});
gridView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
builder.setView(gridView);
} else {
TextView textView = new TextView(context);
textView.setText(context.getString(R.string.no_emoji));
textView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp);
builder.setView(textView);
}
alertDialogEmoji = builder.show();
});
}
int truncate_toots_size = sharedpreferences.getInt(context.getString(R.string.SET_TRUNCATE_TOOTS_SIZE), 0); 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 display_video_preview = sharedpreferences.getBoolean(context.getString(R.string.SET_DISPLAY_VIDEO_PREVIEWS), true);
boolean isModerator = sharedpreferences.getBoolean(Helper.PREF_IS_MODERATOR, false); // boolean isModerator = sharedpreferences.getBoolean(Helper.PREF_IS_MODERATOR, false);
boolean isAdmin = sharedpreferences.getBoolean(Helper.PREF_IS_ADMINISTRATOR, false); // boolean isAdmin = sharedpreferences.getBoolean(Helper.PREF_IS_ADMINISTRATOR, false);
int theme_icons_color = -1; int theme_icons_color = -1;
int theme_statuses_color = -1; int theme_statuses_color = -1;
int theme_boost_header_color = -1; int theme_boost_header_color = -1;
@ -400,6 +520,9 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (theme_statuses_color != -1) { if (theme_statuses_color != -1) {
holder.binding.cardviewContainer.setBackgroundColor(theme_statuses_color); holder.binding.cardviewContainer.setBackgroundColor(theme_statuses_color);
holder.binding.translationLabel.setBackgroundColor(theme_statuses_color); holder.binding.translationLabel.setBackgroundColor(theme_statuses_color);
} else {
holder.binding.cardviewContainer.setBackgroundColor(ContextCompat.getColor(context, R.color.cyanea_primary_dark_reference));
holder.binding.translationLabel.setBackgroundColor(ContextCompat.getColor(context, R.color.cyanea_primary_dark_reference));
} }
if (theme_boost_header_color != -1 && status.reblog != null) { if (theme_boost_header_color != -1 && status.reblog != null) {
holder.binding.statusBoosterInfo.setBackgroundColor(theme_boost_header_color); holder.binding.statusBoosterInfo.setBackgroundColor(theme_boost_header_color);
@ -471,6 +594,35 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
holder.binding.card.setVisibility(View.GONE); 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) { if (minified || !canBeFederated) {
holder.binding.actionButtons.setVisibility(View.GONE); holder.binding.actionButtons.setVisibility(View.GONE);
} else { } else {
@ -491,14 +643,12 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.actionButtonBookmark.setOnClickListener(v -> { holder.binding.actionButtonBookmark.setOnClickListener(v -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = statusList.get(0); Status fetchedStatus = statusList.get(0);
statusesVM.bookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id) statusesVM.bookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, true));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, remote);
});
} else { } else {
Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show();
} }
@ -506,16 +656,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (statusToDeal.bookmarked) { if (statusToDeal.bookmarked) {
statusesVM.unBookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.unBookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNBOOKMARK_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNBOOKMARK_ACTION, statusToDeal, _status, remote);
});
} else { } else {
((SparkButton) v).playAnimation(); ((SparkButton) v).playAnimation();
statusesVM.bookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.bookmark(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.BOOKMARK_ACTION, statusToDeal, _status, remote);
});
} }
} }
}); });
@ -523,7 +668,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.statusUserInfo.setOnClickListener(v -> { holder.binding.statusUserInfo.setOnClickListener(v -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
@ -550,10 +695,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
context.startActivity(intent, options.toBundle()); context.startActivity(intent, options.toBundle());
} }
}); });
holder.binding.statusBoosterAvatar.setOnClickListener(v -> { holder.binding.statusBoosterInfo.setOnClickListener(v -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
@ -599,14 +744,12 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> { alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id, null) statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id, null)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, true));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote);
});
} else { } else {
Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show();
} }
@ -614,15 +757,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (statusToDeal.reblogged) { if (statusToDeal.reblogged) {
statusesVM.unReblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.unReblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, remote);
});
} else { } else {
((SparkButton) v).playAnimation(); ((SparkButton) v).playAnimation();
statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, null) statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, null)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote);
});
} }
} }
dialog.dismiss(); dialog.dismiss();
@ -633,14 +772,12 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id, null) statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id, null)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, true));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote);
});
} else { } else {
Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show();
} }
@ -648,15 +785,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (statusToDeal.reblogged) { if (statusToDeal.reblogged) {
statusesVM.unReblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.unReblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNREBLOG_ACTION, statusToDeal, _status, remote);
});
} else { } else {
((SparkButton) v).playAnimation(); ((SparkButton) v).playAnimation();
statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, null) statusesVM.reblog(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id, null)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.REBLOG_ACTION, statusToDeal, _status, remote);
});
} }
} }
} }
@ -681,14 +814,12 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> { alt_bld.setPositiveButton(R.string.yes, (dialog, id) -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id) statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, true));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote);
});
} else { } else {
Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show();
} }
@ -696,15 +827,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (status.favourited) { if (status.favourited) {
statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, remote);
});
} else { } else {
((SparkButton) v).playAnimation(); ((SparkButton) v).playAnimation();
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote);
});
} }
} }
dialog.dismiss(); dialog.dismiss();
@ -715,14 +842,12 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id) statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, fetchedStatus.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, true));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote);
});
} else { } else {
Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show(); Toasty.info(context, context.getString(R.string.toast_error_search), Toasty.LENGTH_SHORT).show();
} }
@ -730,15 +855,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (statusToDeal.favourited) { if (statusToDeal.favourited) {
statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.unFavourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.UNFAVOURITE_ACTION, statusToDeal, _status, remote);
});
} else { } else {
((SparkButton) v).playAnimation(); ((SparkButton) v).playAnimation();
statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id) statusesVM.favourite(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, statusToDeal.id)
.observe((LifecycleOwner) context, _status -> { .observe((LifecycleOwner) context, _status -> manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, false));
manageAction(context, adapter, statusList, notificationList, CrossActionHelper.TypeOfCrossAction.FAVOURITE_ACTION, statusToDeal, _status, remote);
});
} }
} }
} }
@ -748,11 +869,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
//--- ACCOUNT INFO --- //--- ACCOUNT INFO ---
MastodonHelper.loadPPMastodon(holder.binding.avatar, statusToDeal.account); 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) { holder.binding.displayName.setText(
span_display_name = new SpannableString(statusToDeal.account.username); statusToDeal.account.getSpanDisplayName(context,
} new WeakReference<>(holder.binding.displayName)),
holder.binding.displayName.setText(span_display_name, TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
if (theme_text_header_1_line != -1) { if (theme_text_header_1_line != -1) {
holder.binding.displayName.setTextColor(theme_text_header_1_line); holder.binding.displayName.setTextColor(theme_text_header_1_line);
} }
@ -772,23 +893,15 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} }
if (statusToDeal.account.bot) { if (statusToDeal.account.bot) {
holder.binding.botIcon.setVisibility(View.VISIBLE);
} else { } else {
holder.binding.botIcon.setVisibility(View.GONE); holder.binding.botIcon.setVisibility(View.GONE);
} }
if (statusToDeal.in_reply_to_id != null) { if (statusToDeal.in_reply_to_id != null && timelineType != Timeline.TimeLineEnum.UNKNOWN) {
holder.binding.replyIcon.setVisibility(View.VISIBLE); holder.binding.replyIcon.setVisibility(View.VISIBLE);
} else { } else {
holder.binding.replyIcon.setVisibility(View.GONE); holder.binding.replyIcon.setVisibility(View.GONE);
} }
if (status.isFocused) {
holder.binding.statusInfo.setVisibility(View.VISIBLE);
holder.binding.reblogsCount.setText(String.valueOf(status.reblogs_count));
holder.binding.favoritesCount.setText(String.valueOf(status.favourites_count));
holder.binding.time.setText(Helper.longDateToString(status.created_at));
holder.binding.time.setVisibility(View.VISIBLE);
holder.binding.dateShort.setVisibility(View.GONE);
int ressource = R.drawable.ic_baseline_public_24; int ressource = R.drawable.ic_baseline_public_24;
switch (status.visibility) { switch (status.visibility) {
case "unlisted": case "unlisted":
@ -801,11 +914,21 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
ressource = R.drawable.ic_baseline_mail_24; ressource = R.drawable.ic_baseline_mail_24;
break; break;
} }
if (status.isFocused) {
holder.binding.statusInfo.setVisibility(View.VISIBLE);
holder.binding.reblogsCount.setText(String.valueOf(status.reblogs_count));
holder.binding.favoritesCount.setText(String.valueOf(status.favourites_count));
holder.binding.time.setText(Helper.longDateToString(status.created_at));
holder.binding.time.setVisibility(View.VISIBLE);
holder.binding.dateShort.setVisibility(View.GONE);
holder.binding.visibility.setImageResource(ressource); holder.binding.visibility.setImageResource(ressource);
holder.binding.dateShort.setVisibility(View.GONE); holder.binding.dateShort.setVisibility(View.GONE);
holder.binding.visibilitySmall.setVisibility(View.GONE);
} else { } else {
holder.binding.visibilitySmall.setImageResource(ressource);
holder.binding.statusInfo.setVisibility(View.GONE); holder.binding.statusInfo.setVisibility(View.GONE);
holder.binding.dateShort.setVisibility(View.VISIBLE); holder.binding.dateShort.setVisibility(View.VISIBLE);
holder.binding.visibilitySmall.setVisibility(View.VISIBLE);
holder.binding.dateShort.setText(Helper.dateDiff(context, status.created_at)); holder.binding.dateShort.setText(Helper.dateDiff(context, status.created_at));
holder.binding.time.setVisibility(View.GONE); holder.binding.time.setVisibility(View.GONE);
Helper.absoluteDateTimeReveal(context, holder.binding.dateShort, status.created_at); Helper.absoluteDateTimeReveal(context, holder.binding.dateShort, status.created_at);
@ -817,7 +940,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (expand_cw || expand) { if (expand_cw || expand) {
holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoilerExpand.setVisibility(View.VISIBLE);
holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE);
holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE); holder.binding.spoiler.setText(
statusToDeal.getSpanSpoiler(context,
new WeakReference<>(holder.binding.spoiler)),
TextView.BufferType.SPANNABLE);
statusToDeal.isExpended = true; statusToDeal.isExpended = true;
statusToDeal.isMediaDisplayed = true; statusToDeal.isMediaDisplayed = true;
} else { } else {
@ -828,7 +954,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
}); });
holder.binding.spoilerExpand.setVisibility(View.VISIBLE); holder.binding.spoilerExpand.setVisibility(View.VISIBLE);
holder.binding.spoiler.setVisibility(View.VISIBLE); holder.binding.spoiler.setVisibility(View.VISIBLE);
holder.binding.spoiler.setText(statusToDeal.span_spoiler_text, TextView.BufferType.SPANNABLE);
holder.binding.spoiler.setText(
statusToDeal.getSpanSpoiler(context,
new WeakReference<>(holder.binding.spoiler)),
TextView.BufferType.SPANNABLE);
} }
if (statusToDeal.isExpended) { if (statusToDeal.isExpended) {
holder.binding.spoilerExpand.setText(context.getString(R.string.hide_content)); holder.binding.spoilerExpand.setText(context.getString(R.string.hide_content));
@ -844,13 +974,13 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
//--- BOOSTER INFO --- //--- BOOSTER INFO ---
if (status.reblog != null) { if (status.reblog != null) {
MastodonHelper.loadPPMastodon(holder.binding.statusBoosterAvatar, status.account); 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) { holder.binding.statusBoosterDisplayName.setText(
span_display_name_boost = new SpannableString(status.account.username); status.account.getSpanDisplayName(context,
} new WeakReference<>(holder.binding.statusBoosterDisplayName)),
holder.binding.statusBoosterDisplayName.setText(span_display_name_boost, TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE); holder.binding.statusBoosterInfo.setVisibility(View.VISIBLE);
holder.binding.boosterDivider.setVisibility(View.VISIBLE);
if (theme_text_header_1_line != -1) { if (theme_text_header_1_line != -1) {
holder.binding.statusBoosterDisplayName.setTextColor(theme_text_header_1_line); holder.binding.statusBoosterDisplayName.setTextColor(theme_text_header_1_line);
} }
@ -860,7 +990,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} }
} else { } else {
holder.binding.statusBoosterInfo.setVisibility(View.GONE); holder.binding.statusBoosterInfo.setVisibility(View.GONE);
holder.binding.boosterDivider.setVisibility(View.GONE);
} }
//--- BOOST VISIBILITY --- //--- BOOST VISIBILITY ---
switch (statusToDeal.visibility) { switch (statusToDeal.visibility) {
@ -880,7 +1009,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
break; break;
} }
//--- MAIN CONTENT --- //--- MAIN CONTENT ---
holder.binding.statusContent.setText(statusToDeal.span_content, TextView.BufferType.SPANNABLE); holder.binding.statusContent.setText(
statusToDeal.getSpanContent(context,
new WeakReference<>(holder.binding.statusContent)),
TextView.BufferType.SPANNABLE);
if (truncate_toots_size > 0) { if (truncate_toots_size > 0) {
holder.binding.statusContent.setMaxLines(truncate_toots_size); holder.binding.statusContent.setMaxLines(truncate_toots_size);
holder.binding.statusContent.setEllipsize(TextUtils.TruncateAt.END); holder.binding.statusContent.setEllipsize(TextUtils.TruncateAt.END);
@ -911,7 +1043,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} }
if (statusToDeal.translationContent != null) { if (statusToDeal.translationContent != null) {
holder.binding.containerTrans.setVisibility(View.VISIBLE); holder.binding.containerTrans.setVisibility(View.VISIBLE);
holder.binding.statusContentTranslated.setText(statusToDeal.span_translate, TextView.BufferType.SPANNABLE); holder.binding.statusContentTranslated.setText(
statusToDeal.getSpanTranslate(context,
new WeakReference<>(holder.binding.statusContentTranslated)),
TextView.BufferType.SPANNABLE);
} else { } else {
holder.binding.containerTrans.setVisibility(View.GONE); holder.binding.containerTrans.setVisibility(View.GONE);
} }
@ -925,6 +1060,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.statusContent.setVisibility(View.GONE); holder.binding.statusContent.setVisibility(View.GONE);
holder.binding.mediaContainer.setVisibility(View.GONE); holder.binding.mediaContainer.setVisibility(View.GONE);
} }
LayoutInflater inflater = ((Activity) context).getLayoutInflater(); LayoutInflater inflater = ((Activity) context).getLayoutInflater();
//--- MEDIA ATTACHMENT --- //--- MEDIA ATTACHMENT ---
if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) { if (statusToDeal.media_attachments != null && statusToDeal.media_attachments.size() > 0) {
@ -994,17 +1130,19 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (!mediaObfuscated(statusToDeal) || expand_media) { if (!mediaObfuscated(statusToDeal) || expand_media) {
layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24); layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24);
Glide.with(layoutMediaBinding.media.getContext()) RequestBuilder<Drawable> requestBuilder = Glide.with(layoutMediaBinding.media.getContext())
.load(statusToDeal.media_attachments.get(0).preview_url) .load(statusToDeal.media_attachments.get(0).preview_url)
.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))) .apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)));
.into(layoutMediaBinding.media); if (!fullAttachement) {
requestBuilder = requestBuilder.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)));
}
requestBuilder.into(layoutMediaBinding.media);
} else { } else {
layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24); layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24);
Glide.with(layoutMediaBinding.media.getContext()) Glide.with(layoutMediaBinding.media.getContext())
.load(statusToDeal.media_attachments.get(0).preview_url) .load(statusToDeal.media_attachments.get(0).preview_url)
.apply(new RequestOptions().transform(new BlurTransformation(50, 3))) .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 CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context))))
.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)))
.into(layoutMediaBinding.media); .into(layoutMediaBinding.media);
} }
layoutMediaBinding.viewHide.setOnClickListener(v -> { layoutMediaBinding.viewHide.setOnClickListener(v -> {
@ -1046,17 +1184,18 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
lp.setMargins(0, 0, (int) Helper.convertDpToPixel(5, context), 0); lp.setMargins(0, 0, (int) Helper.convertDpToPixel(5, context), 0);
if (!mediaObfuscated(statusToDeal) || expand_media) { if (!mediaObfuscated(statusToDeal) || expand_media) {
layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24); layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_24);
Glide.with(layoutMediaBinding.media.getContext()) RequestBuilder<Drawable> requestBuilder = Glide.with(layoutMediaBinding.media.getContext())
.load(attachment.preview_url) .load(attachment.preview_url)
.apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context))));
.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY))) if (!fullAttachement) {
.into(layoutMediaBinding.media); requestBuilder = requestBuilder.apply(new RequestOptions().transform(new GlideFocus(focusX, focusY)));
}
requestBuilder.into(layoutMediaBinding.media);
} else { } else {
layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24); layoutMediaBinding.viewHide.setImageResource(R.drawable.ic_baseline_visibility_off_24);
Glide.with(layoutMediaBinding.media.getContext()) Glide.with(layoutMediaBinding.media.getContext())
.load(attachment.preview_url) .load(attachment.preview_url)
.apply(new RequestOptions().transform(new BlurTransformation(50, 3))) .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)))) // .apply(new RequestOptions().transform(new CenterCrop(), new RoundedCorners((int) Helper.convertDpToPixel(3, context))))
.into(layoutMediaBinding.media); .into(layoutMediaBinding.media);
} }
@ -1096,7 +1235,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.reblogInfo.setOnClickListener(v -> { holder.binding.reblogInfo.setOnClickListener(v -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
@ -1123,7 +1262,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.favouriteInfo.setOnClickListener(v -> { holder.binding.favouriteInfo.setOnClickListener(v -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
@ -1174,7 +1313,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
pollItemBinding.pollItemPercent.setTextColor(theme_text_color); pollItemBinding.pollItemPercent.setTextColor(theme_text_color);
pollItemBinding.pollItemText.setTextColor(theme_text_color); pollItemBinding.pollItemText.setTextColor(theme_text_color);
} }
pollItemBinding.pollItemText.setText(pollItem.span_title, TextView.BufferType.SPANNABLE); pollItemBinding.pollItemText.setText(
pollItem.getSpanTitle(context, statusToDeal,
new WeakReference<>(pollItemBinding.pollItemText)),
TextView.BufferType.SPANNABLE);
pollItemBinding.pollItemValue.setProgress((int) value); pollItemBinding.pollItemValue.setProgress((int) value);
if (pollItem.votes_count == greaterValue) { if (pollItem.votes_count == greaterValue) {
pollItemBinding.pollItemPercent.setTypeface(null, Typeface.BOLD); pollItemBinding.pollItemPercent.setTypeface(null, Typeface.BOLD);
@ -1202,7 +1344,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
for (Poll.PollItem pollOption : statusToDeal.poll.options) { for (Poll.PollItem pollOption : statusToDeal.poll.options) {
CheckBox cb = new CheckBox(context); CheckBox cb = new CheckBox(context);
cb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); cb.setButtonTintList(ThemeHelper.getButtonColorStateList(context));
cb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); cb.setText(
pollOption.getSpanTitle(context, statusToDeal,
new WeakReference<>(cb)),
TextView.BufferType.SPANNABLE);
holder.binding.poll.multipleChoice.addView(cb); holder.binding.poll.multipleChoice.addView(cb);
} }
holder.binding.poll.multipleChoice.setVisibility(View.VISIBLE); holder.binding.poll.multipleChoice.setVisibility(View.VISIBLE);
@ -1213,7 +1358,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
for (Poll.PollItem pollOption : statusToDeal.poll.options) { for (Poll.PollItem pollOption : statusToDeal.poll.options) {
RadioButton rb = new RadioButton(context); RadioButton rb = new RadioButton(context);
rb.setButtonTintList(ThemeHelper.getButtonColorStateList(context)); rb.setButtonTintList(ThemeHelper.getButtonColorStateList(context));
rb.setText(pollOption.span_title, TextView.BufferType.SPANNABLE); rb.setText(
pollOption.getSpanTitle(context, statusToDeal,
new WeakReference<>(rb)),
TextView.BufferType.SPANNABLE);
holder.binding.poll.singleChoiceRadioGroup.addView(rb); holder.binding.poll.singleChoiceRadioGroup.addView(rb);
} }
holder.binding.poll.singleChoiceRadioGroup.setVisibility(View.VISIBLE); holder.binding.poll.singleChoiceRadioGroup.setVisibility(View.VISIBLE);
@ -1256,7 +1405,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
//Vote on the poll //Vote on the poll
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
@ -1322,9 +1471,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
return false; return false;
}); });
if (!minified && canBeFederated) { if (!minified && canBeFederated) {
holder.binding.mainContainer.setOnClickListener(v -> { holder.binding.mainContainer.setOnClickListener(v -> holder.binding.statusContent.callOnClick());
holder.binding.statusContent.callOnClick();
});
holder.binding.statusContent.setOnClickListener(v -> { holder.binding.statusContent.setOnClickListener(v -> {
if (status.isFocused || v.getTag() == SpannableHelper.CLICKABLE_SPAN) { if (status.isFocused || v.getTag() == SpannableHelper.CLICKABLE_SPAN) {
if (v.getTag() == SpannableHelper.CLICKABLE_SPAN) { if (v.getTag() == SpannableHelper.CLICKABLE_SPAN) {
@ -1340,7 +1487,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
} else { } else {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = results.statuses.get(0); Status fetchedStatus = results.statuses.get(0);
@ -1446,11 +1593,11 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
builderInner.show(); builderInner.show();
} else if (itemId == R.id.action_schedule_boost) { } else if (itemId == R.id.action_schedule_boost) {
MastodonHelper.scheduleBoost(context, MastodonHelper.ScheduleType.BOOST, statusToDeal, null, null); MastodonHelper.scheduleBoost(context, MastodonHelper.ScheduleType.BOOST, statusToDeal, null, null);
} else if (itemId == R.id.action_admin) { } /*else if (itemId == R.id.action_admin) {
/* Intent intent = new Intent(context, AccountReportActivity.class); Intent intent = new Intent(context, AccountReportActivity.class);
intent.putExtra(Helper.ARG_ACCOUNT, statusToDeal.account); intent.putExtra(Helper.ARG_ACCOUNT, statusToDeal.account);
context.startActivity(intent);*/ context.startActivity(intent);
} else if (itemId == R.id.action_open_browser) { } */ else if (itemId == R.id.action_open_browser) {
Helper.openBrowser(context, statusToDeal.url); Helper.openBrowser(context, statusToDeal.url);
} else if (itemId == R.id.action_remove) { } else if (itemId == R.id.action_remove) {
AlertDialog.Builder builderInner = new AlertDialog.Builder(context, Helper.dialogStyle()); AlertDialog.Builder builderInner = new AlertDialog.Builder(context, Helper.dialogStyle());
@ -1540,12 +1687,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
if (translate.getTranslatedContent() != null) { if (translate.getTranslatedContent() != null) {
statusToDeal.translationShown = true; statusToDeal.translationShown = true;
statusToDeal.translationContent = translate.getTranslatedContent(); statusToDeal.translationContent = translate.getTranslatedContent();
new Thread(() -> { adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
SpannableHelper.convertStatus(context.getApplicationContext(), statusToDeal);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> adapter.notifyItemChanged(getPositionAsync(notificationList, statusList, statusToDeal));
mainHandler.post(myRunnable);
}).start();
} else { } else {
Toasty.error(context, context.getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); Toasty.error(context, context.getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show();
} }
@ -1631,7 +1773,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.binding.actionButtonReply.setOnClickListener(v -> { holder.binding.actionButtonReply.setOnClickListener(v -> {
if (remote) { if (remote) {
Toasty.info(context, context.getString(R.string.retrieve_remote_status), Toasty.LENGTH_SHORT).show(); 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 -> { .observe((LifecycleOwner) context, results -> {
if (results.statuses != null && results.statuses.size() > 0) { if (results.statuses != null && results.statuses.size() > 0) {
Status fetchedStatus = statusList.get(0); Status fetchedStatus = statusList.get(0);
@ -1657,6 +1799,7 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
holder.bindingReport.checkbox.setChecked(status.isChecked); holder.bindingReport.checkbox.setChecked(status.isChecked);
holder.bindingReport.checkbox.setOnClickListener(v -> status.isChecked = !status.isChecked); holder.bindingReport.checkbox.setOnClickListener(v -> status.isChecked = !status.isChecked);
} }
} }
private static boolean mediaObfuscated(Status status) { private static boolean mediaObfuscated(Status status) {
@ -1723,6 +1866,34 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
return position; return position;
} }
/**
* Will manage the current position of the element in the adapter. Action is async, and position might have changed
*
* @param notificationList List<Notification> - Not null when calling from notification adapter
* @param statusList ist<Status> statusList - Not null when calling from status adapter
* @param id String - Current status
* @return int - position in real time
*/
public static int getPositionAsync(List<Notification> notificationList, List<Status> 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 @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
if (timelineType == Timeline.TimeLineEnum.ART) { if (timelineType == Timeline.TimeLineEnum.ART) {
@ -1783,36 +1954,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class); StatusesVM statusesVM = new ViewModelProvider((ViewModelStoreOwner) context).get(StatusesVM.class);
SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class); SearchVM searchVM = new ViewModelProvider((ViewModelStoreOwner) context).get(SearchVM.class);
statusManagement(context, statusesVM, searchVM, holder, this, statusList, null, status, timelineType, minified, canBeFederated); statusManagement(context, statusesVM, searchVM, holder, this, statusList, null, status, timelineType, minified, canBeFederated);
if (holder.timer != null) {
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() {
@Override
public void run() {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> holder.binding.statusContent.invalidate();
mainHandler.post(myRunnable);
}
}, 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) { } else if (viewHolder.getItemViewType() == STATUS_ART) {
StatusViewHolder holder = (StatusViewHolder) viewHolder; StatusViewHolder holder = (StatusViewHolder) viewHolder;
MastodonHelper.loadPPMastodon(holder.bindingArt.artPp, status.account); MastodonHelper.loadPPMastodon(holder.bindingArt.artPp, status.account);
@ -1820,7 +1961,10 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
.load(status.art_attachment.preview_url) .load(status.art_attachment.preview_url)
.apply(new RequestOptions().transform(new RoundedCorners((int) Helper.convertDpToPixel(3, context)))) .apply(new RequestOptions().transform(new RoundedCorners((int) Helper.convertDpToPixel(3, context))))
.into(holder.bindingArt.artMedia); .into(holder.bindingArt.artMedia);
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)),
TextView.BufferType.SPANNABLE);
holder.bindingArt.artUsername.setText(String.format(Locale.getDefault(), "@%s", status.account.acct)); holder.bindingArt.artUsername.setText(String.format(Locale.getDefault(), "@%s", status.account.acct));
holder.bindingArt.artPp.setOnClickListener(v -> { holder.bindingArt.artPp.setOnClickListener(v -> {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
@ -1878,12 +2022,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
@Override @Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
super.onViewRecycled(holder); super.onViewRecycled(holder);
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 { public interface FetchMoreCallBack {
@ -1899,8 +2037,6 @@ public class StatusAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
DrawerFetchMoreBinding bindingFetchMore; DrawerFetchMoreBinding bindingFetchMore;
DrawerStatusNotificationBinding bindingNotification; DrawerStatusNotificationBinding bindingNotification;
DrawerStatusArtBinding bindingArt; DrawerStatusArtBinding bindingArt;
Timer timer;
Timer dateTimer;
StatusViewHolder(DrawerStatusBinding itemView) { StatusViewHolder(DrawerStatusBinding itemView) {
super(itemView.getRoot()); super(itemView.getRoot());

View file

@ -122,7 +122,7 @@ public class FragmentMedia extends Fragment {
binding.mediaPicture.setVisibility(View.VISIBLE); binding.mediaPicture.setVisibility(View.VISIBLE);
binding.mediaPicture.setTransitionName(attachment.url); binding.mediaPicture.setTransitionName(attachment.url);
if (Helper.isValidContextForGlide(requireActivity())) { if (Helper.isValidContextForGlide(requireActivity()) && isAdded()) {
Glide.with(requireActivity()) Glide.with(requireActivity())
.asBitmap() .asBitmap()
.dontTransform() .dontTransform()
@ -142,7 +142,7 @@ public class FragmentMedia extends Fragment {
binding.mediaPicture.setVisibility(View.VISIBLE); binding.mediaPicture.setVisibility(View.VISIBLE);
binding.pbarInf.setIndeterminate(true); binding.pbarInf.setIndeterminate(true);
binding.loader.setVisibility(View.VISIBLE); binding.loader.setVisibility(View.VISIBLE);
if (Helper.isValidContextForGlide(requireActivity())) { if (Helper.isValidContextForGlide(requireActivity()) && isAdded()) {
Glide.with(requireActivity()) Glide.with(requireActivity())
.asBitmap() .asBitmap()
.dontTransform() .dontTransform()
@ -312,7 +312,6 @@ public class FragmentMedia extends Fragment {
timer.cancel(); timer.cancel();
timer = null; timer = null;
} }
binding = null;
} }
@Override @Override

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
@ -31,16 +34,28 @@ public class FragmentLanguageSettings extends PreferenceFragmentCompat implement
createPref(); createPref();
} }
@SuppressLint("ApplySharedPref")
private void createPref() { private void createPref() {
ListPreference SET_DEFAULT_LOCALE_NEW = findPreference(getString(R.string.SET_DEFAULT_LOCALE_NEW)); ListPreference SET_DEFAULT_LOCALE_NEW = findPreference(getString(R.string.SET_DEFAULT_LOCALE_NEW));
if (SET_DEFAULT_LOCALE_NEW != null) { if (SET_DEFAULT_LOCALE_NEW != null) {
SET_DEFAULT_LOCALE_NEW.getContext().setTheme(Helper.dialogStyle()); 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 @Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 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(); requireActivity().recreate();
Helper.recreateMainActivity(requireActivity()); Helper.recreateMainActivity(requireActivity());
} }

View file

@ -21,8 +21,6 @@ import android.content.BroadcastReceiver;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -45,7 +43,6 @@ import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.databinding.FragmentPaginationBinding;
import app.fedilab.android.helper.DividerDecoration; import app.fedilab.android.helper.DividerDecoration;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.ui.drawer.StatusAdapter;
import app.fedilab.android.viewmodel.mastodon.StatusesVM; import app.fedilab.android.viewmodel.mastodon.StatusesVM;
@ -98,14 +95,10 @@ public class FragmentMastodonContext extends Fragment {
} }
} else if (statusPosted != null && statusAdapter != null) { } else if (statusPosted != null && statusAdapter != null) {
if (requireActivity() instanceof ContextActivity) { if (requireActivity() instanceof ContextActivity) {
new Thread(() -> {
Status convertStatus = SpannableHelper.convertStatus(context, statusPosted);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
int i = 0; int i = 0;
for (Status status : statuses) { for (Status status : statuses) {
if (status.id.equals(convertStatus.in_reply_to_id)) { if (status.id.equals(statusPosted.in_reply_to_id)) {
statuses.add((i + 1), convertStatus); statuses.add((i + 1), statusPosted);
statusAdapter.notifyItemInserted((i + 1)); statusAdapter.notifyItemInserted((i + 1));
if (requireActivity() instanceof ContextActivity) { if (requireActivity() instanceof ContextActivity) {
//Redraw decorations //Redraw decorations
@ -115,9 +108,6 @@ public class FragmentMastodonContext extends Fragment {
} }
i++; i++;
} }
};
mainHandler.post(myRunnable);
}).start();
} }
} }
} }

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.app.NotificationManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.service.notification.StatusBarNotification;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; 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 * Intialize the view for notifications
* *
@ -456,6 +437,8 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
} }
int position = 0; int position = 0;
//We loop through messages already in the timeline //We loop through messages already in the timeline
if (this.notificationList != null) {
notificationAdapter.notifyItemRangeChanged(0, this.notificationList.size());
for (Notification notificationsAlreadyPresent : this.notificationList) { 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 //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 //Pinned messages are ignored because their date can be older
@ -476,6 +459,7 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
notificationAdapter.notifyItemInserted(position); notificationAdapter.notifyItemInserted(position);
return NOTIFICATION__AT_THE_BOTTOM; return NOTIFICATION__AT_THE_BOTTOM;
} }
}
return position; return position;
} }
@ -491,8 +475,8 @@ public class FragmentMastodonNotification extends Fragment implements Notificati
} }
}).start(); }).start();
} }
super.onDestroyView();
LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action); LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(receive_action);
super.onDestroyView();
} }
@Override @Override

View file

@ -30,7 +30,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@ -57,7 +56,6 @@ import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.FragmentPaginationBinding; import app.fedilab.android.databinding.FragmentPaginationBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.StatusAdapter; import app.fedilab.android.ui.drawer.StatusAdapter;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.viewmodel.mastodon.AccountsVM;
@ -119,15 +117,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
statusAdapter.notifyItemRemoved(position); statusAdapter.notifyItemRemoved(position);
} }
} else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) { } else if (statusPosted != null && statusAdapter != null && timelineType == Timeline.TimeLineEnum.HOME) {
new Thread(() -> { statuses.add(0, statusPosted);
Status convertStatus = SpannableHelper.convertStatus(context, statusPosted);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
statuses.add(0, convertStatus);
statusAdapter.notifyItemInserted(0); statusAdapter.notifyItemInserted(0);
};
mainHandler.post(myRunnable);
}).start();
} }
} }
} }
@ -184,15 +175,12 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
public void scrollToTop() { public void scrollToTop() {
if (binding != null) { if (binding != null) {
binding.recyclerView.scrollToPosition(0); binding.swipeContainer.setRefreshing(true);
flagLoading = false;
route(DIRECTION.SCROLL_TOP, true);
} }
} }
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
public View onCreateView(@NonNull LayoutInflater inflater, public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { ViewGroup container, Bundle savedInstanceState) {
@ -437,6 +425,9 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else if (direction == DIRECTION.BOTTOM) { } else if (direction == DIRECTION.BOTTOM) {
flagLoading = true; flagLoading = true;
} }
if (direction == DIRECTION.SCROLL_TOP) {
binding.recyclerView.scrollToPosition(0);
}
} }
/** /**
@ -473,7 +464,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
statusFetchMore.isFetchMore = true; statusFetchMore.isFetchMore = true;
statusFetchMore.id = Helper.generateString(); statusFetchMore.id = Helper.generateString();
int insertAt; int insertAt;
if (direction == DIRECTION.REFRESH || direction == DIRECTION.BOTTOM) { if (direction == DIRECTION.REFRESH || direction == DIRECTION.BOTTOM || direction == DIRECTION.SCROLL_TOP) {
insertAt = lastInsertedPosition; insertAt = lastInsertedPosition;
} else { } else {
insertAt = initialInsertedPosition; insertAt = initialInsertedPosition;
@ -499,6 +490,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
return STATUS_PRESENT; return STATUS_PRESENT;
} }
int position = 0; int position = 0;
if (this.statuses != null) {
statusAdapter.notifyItemRangeChanged(0, this.statuses.size());
//We loop through messages already in the timeline //We loop through messages already in the timeline
for (Status statusAlreadyPresent : this.statuses) { 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 //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
@ -520,6 +513,8 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
statusAdapter.notifyItemInserted(position); statusAdapter.notifyItemInserted(position);
return STATUS_AT_THE_BOTTOM; return STATUS_AT_THE_BOTTOM;
} }
}
return position; return position;
} }
@ -627,11 +622,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -647,11 +642,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPublic(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, false, true, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -669,11 +664,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
flagLoading = false; flagLoading = false;
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getNitter(remoteInstance, pinnedTimeline.remoteInstance.host, null) timelinesVM.getNitter(remoteInstance, pinnedTimeline.remoteInstance.host, null)
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -692,11 +687,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
flagLoading = false; flagLoading = false;
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getMisskey(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getMisskey(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -713,11 +708,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.BOTTOM, false));
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
flagLoading = false; flagLoading = false;
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPeertube(remoteInstance, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -733,11 +728,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getPublic(null, remoteInstance, true, false, false, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -754,11 +749,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, list_id, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -778,11 +773,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
} else if (direction == DIRECTION.TOP) { } else if (direction == DIRECTION.TOP) {
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, fetchingMissing ? min_id_fetch_more : min_id, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing)); .observe(getViewLifecycleOwner(), statusesBottom -> dealWithPagination(statusesBottom, DIRECTION.TOP, fetchingMissing));
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, null, MastodonHelper.statusesPerCall(requireActivity())) timelinesVM.getHashTag(BaseMainActivity.currentToken, BaseMainActivity.currentInstance, tagTimeline.name, false, tagTimeline.isART, tagTimeline.all, tagTimeline.any, tagTimeline.none, null, null, null, MastodonHelper.statusesPerCall(requireActivity()))
.observe(getViewLifecycleOwner(), statusesRefresh -> { .observe(getViewLifecycleOwner(), statusesRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusesRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusesRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusesRefresh); initializeStatusesCommonView(statusesRefresh);
} }
@ -964,11 +959,11 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
.observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, true)); .observe(getViewLifecycleOwner(), statusesTop -> dealWithPagination(statusesTop, DIRECTION.TOP, true));
} }
} else if (direction == DIRECTION.REFRESH) { } else if (direction == DIRECTION.REFRESH || direction == DIRECTION.SCROLL_TOP) {
timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, true, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), false) timelinesVM.getHome(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, true, null, null, null, MastodonHelper.statusesPerCall(requireActivity()), false)
.observe(getViewLifecycleOwner(), statusRefresh -> { .observe(getViewLifecycleOwner(), statusRefresh -> {
if (statusAdapter != null) { if (statusAdapter != null) {
dealWithPagination(statusRefresh, DIRECTION.REFRESH, true); dealWithPagination(statusRefresh, direction, true);
} else { } else {
initializeStatusesCommonView(statusRefresh); initializeStatusesCommonView(statusRefresh);
} }
@ -1029,6 +1024,7 @@ public class FragmentMastodonTimeline extends Fragment implements StatusAdapter.
public enum DIRECTION { public enum DIRECTION {
TOP, TOP,
BOTTOM, BOTTOM,
REFRESH REFRESH,
SCROLL_TOP
} }
} }

View file

@ -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, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint; import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat; import androidx.core.content.res.ResourcesCompat;
@ -238,6 +240,20 @@ public class FragmentNotificationContainer extends Fragment {
return binding.getRoot(); 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() { public void scrollToTop() {
if (binding != null) { 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);
}
} }

View file

@ -52,7 +52,6 @@ import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.client.entities.api.Token; import app.fedilab.android.client.entities.api.Token;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Call; import retrofit2.Call;
@ -306,9 +305,6 @@ public class AccountsVM extends AndroidViewModel {
} }
} }
Account finalAccount = account; Account finalAccount = account;
if (finalAccount != null) {
SpannableHelper.convertAccount(getApplication().getApplicationContext(), finalAccount);
}
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> accountMutableLiveData.setValue(finalAccount); Runnable myRunnable = () -> accountMutableLiveData.setValue(finalAccount);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
@ -342,7 +338,7 @@ public class AccountsVM extends AndroidViewModel {
try { try {
Response<List<Status>> accountStatusesResponse = accountStatusesCall.execute(); Response<List<Status>> accountStatusesResponse = accountStatusesCall.execute();
if (accountStatusesResponse.isSuccessful()) { if (accountStatusesResponse.isSuccessful()) {
statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), accountStatusesResponse.body()); statusList = accountStatusesResponse.body();
pagination = MastodonHelper.getPagination(accountStatusesResponse.headers()); pagination = MastodonHelper.getPagination(accountStatusesResponse.headers());
} }
@ -378,7 +374,7 @@ public class AccountsVM extends AndroidViewModel {
try { try {
Response<List<Account>> followersResponse = followersCall.execute(); Response<List<Account>> followersResponse = followersCall.execute();
if (followersResponse.isSuccessful()) { if (followersResponse.isSuccessful()) {
accountList = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), followersResponse.body()); accountList = followersResponse.body();
pagination = MastodonHelper.getPagination(followersResponse.headers()); pagination = MastodonHelper.getPagination(followersResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -414,7 +410,7 @@ public class AccountsVM extends AndroidViewModel {
try { try {
Response<List<Account>> followingResponse = followingCall.execute(); Response<List<Account>> followingResponse = followingCall.execute();
if (followingResponse.isSuccessful()) { if (followingResponse.isSuccessful()) {
accountList = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), followingResponse.body()); accountList = followingResponse.body();
pagination = MastodonHelper.getPagination(followingResponse.headers()); pagination = MastodonHelper.getPagination(followingResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -883,11 +879,6 @@ public class AccountsVM extends AndroidViewModel {
} }
} }
List<Account> finalAccountList = accountList; List<Account> finalAccountList = accountList;
if (finalAccountList != null) {
for (Account account : finalAccountList) {
SpannableHelper.convertAccount(getApplication().getApplicationContext(), account);
}
}
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> accountListMutableLiveData.setValue(finalAccountList); Runnable myRunnable = () -> accountListMutableLiveData.setValue(finalAccountList);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
@ -912,7 +903,7 @@ public class AccountsVM extends AndroidViewModel {
Response<List<Status>> bookmarksResponse = bookmarksCall.execute(); Response<List<Status>> bookmarksResponse = bookmarksCall.execute();
if (bookmarksResponse.isSuccessful()) { if (bookmarksResponse.isSuccessful()) {
statusList = bookmarksResponse.body(); statusList = bookmarksResponse.body();
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); statuses.statuses = statusList;
statuses.pagination = MastodonHelper.getPagination(bookmarksResponse.headers()); statuses.pagination = MastodonHelper.getPagination(bookmarksResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -943,7 +934,7 @@ public class AccountsVM extends AndroidViewModel {
Response<List<Status>> favouritesResponse = favouritesCall.execute(); Response<List<Status>> favouritesResponse = favouritesCall.execute();
if (favouritesResponse.isSuccessful()) { if (favouritesResponse.isSuccessful()) {
statusList = favouritesResponse.body(); statusList = favouritesResponse.body();
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); statuses.statuses = statusList;
statuses.pagination = MastodonHelper.getPagination(favouritesResponse.headers()); statuses.pagination = MastodonHelper.getPagination(favouritesResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -975,7 +966,7 @@ public class AccountsVM extends AndroidViewModel {
Response<List<Account>> mutesResponse = mutesCall.execute(); Response<List<Account>> mutesResponse = mutesCall.execute();
if (mutesResponse.isSuccessful()) { if (mutesResponse.isSuccessful()) {
accountList = mutesResponse.body(); accountList = mutesResponse.body();
accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); accounts.accounts = accountList;
accounts.pagination = MastodonHelper.getPagination(mutesResponse.headers()); accounts.pagination = MastodonHelper.getPagination(mutesResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -1007,7 +998,7 @@ public class AccountsVM extends AndroidViewModel {
Response<List<Account>> blocksResponse = blocksCall.execute(); Response<List<Account>> blocksResponse = blocksCall.execute();
if (blocksResponse.isSuccessful()) { if (blocksResponse.isSuccessful()) {
accountList = blocksResponse.body(); accountList = blocksResponse.body();
accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); accounts.accounts = accountList;
accounts.pagination = MastodonHelper.getPagination(blocksResponse.headers()); accounts.pagination = MastodonHelper.getPagination(blocksResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -1303,7 +1294,7 @@ public class AccountsVM extends AndroidViewModel {
Response<List<Account>> followRequestsResponse = followRequestsCall.execute(); Response<List<Account>> followRequestsResponse = followRequestsCall.execute();
if (followRequestsResponse.isSuccessful()) { if (followRequestsResponse.isSuccessful()) {
accountList = followRequestsResponse.body(); accountList = followRequestsResponse.body();
accounts.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountList); accounts.accounts = accountList;
accounts.pagination = MastodonHelper.getPagination(followRequestsResponse.headers()); accounts.pagination = MastodonHelper.getPagination(followRequestsResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {

View file

@ -23,16 +23,12 @@ import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import app.fedilab.android.client.endpoints.MastodonAnnouncementsService; import app.fedilab.android.client.endpoints.MastodonAnnouncementsService;
import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.client.entities.api.Announcement;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.SpannableHelper;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Response; import retrofit2.Response;
@ -55,7 +51,6 @@ public class AnnouncementsVM extends AndroidViewModel {
} }
private MastodonAnnouncementsService init(@NonNull String instance) { private MastodonAnnouncementsService init(@NonNull String instance) {
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();
Retrofit retrofit = new Retrofit.Builder() Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + instance + "/api/v1/") .baseUrl("https://" + instance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder())) .addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
@ -83,7 +78,6 @@ public class AnnouncementsVM extends AndroidViewModel {
Response<List<Announcement>> getAnnouncementsResponse = getAnnouncementsCall.execute(); Response<List<Announcement>> getAnnouncementsResponse = getAnnouncementsCall.execute();
if (getAnnouncementsResponse.isSuccessful()) { if (getAnnouncementsResponse.isSuccessful()) {
announcementList = getAnnouncementsResponse.body(); announcementList = getAnnouncementsResponse.body();
SpannableHelper.convertAnnouncement(getApplication(), announcementList);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -35,7 +35,6 @@ import app.fedilab.android.client.entities.api.Notifications;
import app.fedilab.android.client.entities.api.PushSubscription; import app.fedilab.android.client.entities.api.PushSubscription;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.TimelineHelper; import app.fedilab.android.helper.TimelineHelper;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Call; import retrofit2.Call;
@ -103,15 +102,6 @@ public class NotificationsVM extends AndroidViewModel {
if (notificationsResponse.isSuccessful()) { if (notificationsResponse.isSuccessful()) {
List<Notification> notFilteredNotifications = notificationsResponse.body(); List<Notification> notFilteredNotifications = notificationsResponse.body();
notifications.notifications = TimelineHelper.filterNotification(getApplication().getApplicationContext(), notFilteredNotifications); 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()); notifications.pagination = MastodonHelper.getPagination(notificationsResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -147,9 +137,6 @@ public class NotificationsVM extends AndroidViewModel {
Response<Notification> notificationResponse = notificationCall.execute(); Response<Notification> notificationResponse = notificationCall.execute();
if (notificationResponse.isSuccessful()) { if (notificationResponse.isSuccessful()) {
notification = notificationResponse.body(); notification = notificationResponse.body();
if (notification != null) {
notification.status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), notification.status);
}
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -36,7 +36,6 @@ import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.SpannableHelper;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Response; import retrofit2.Response;
@ -112,13 +111,9 @@ public class SearchVM extends AndroidViewModel {
if (results != null) { if (results != null) {
if (results.statuses == null) { if (results.statuses == null) {
results.statuses = new ArrayList<>(); results.statuses = new ArrayList<>();
} else {
results.statuses = SpannableHelper.convertStatus(getApplication(), results.statuses);
} }
if (results.accounts == null) { if (results.accounts == null) {
results.accounts = new ArrayList<>(); results.accounts = new ArrayList<>();
} else {
results.accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), results.accounts);
} }
if (results.hashtags == null) { if (results.hashtags == null) {
results.hashtags = new ArrayList<>(); results.hashtags = new ArrayList<>();
@ -144,7 +139,6 @@ public class SearchVM extends AndroidViewModel {
try { try {
results.statuses = new ArrayList<>(); results.statuses = new ArrayList<>();
List<Status> statuses = new StatusCache(getApplication()).searchStatus(StatusCache.CacheEnum.HOME, instance, userId, q); List<Status> statuses = new StatusCache(getApplication()).searchStatus(StatusCache.CacheEnum.HOME, instance, userId, q);
statuses = SpannableHelper.convertStatus(getApplication(), statuses);
results.statuses.addAll(statuses); results.statuses.addAll(statuses);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -47,7 +47,6 @@ import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.TimelineHelper; import app.fedilab.android.helper.TimelineHelper;
import okhttp3.Headers; import okhttp3.Headers;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
@ -279,7 +278,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -356,13 +355,7 @@ public class StatusesVM extends AndroidViewModel {
context = contextResponse.body(); context = contextResponse.body();
if (context != null) { if (context != null) {
TimelineHelper.filterStatus(getApplication().getApplicationContext(), context.descendants, TimelineHelper.FilterTimeLineType.CONTEXT); 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); 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 { try {
Response<List<Account>> accountsResponse = accountsCall.execute(); Response<List<Account>> accountsResponse = accountsCall.execute();
if (accountsResponse.isSuccessful()) { if (accountsResponse.isSuccessful()) {
accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountsResponse.body()); accounts = accountsResponse.body();
} }
headers = accountsResponse.headers(); headers = accountsResponse.headers();
} catch (Exception e) { } catch (Exception e) {
@ -451,7 +444,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<List<Account>> accountsResponse = accountsCall.execute(); Response<List<Account>> accountsResponse = accountsCall.execute();
if (accountsResponse.isSuccessful()) { if (accountsResponse.isSuccessful()) {
accounts = SpannableHelper.convertAccounts(getApplication().getApplicationContext(), accountsResponse.body()); accounts = accountsResponse.body();
} }
headers = accountsResponse.headers(); headers = accountsResponse.headers();
} catch (Exception e) { } catch (Exception e) {
@ -461,7 +454,9 @@ public class StatusesVM extends AndroidViewModel {
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Accounts accountsPagination = new Accounts(); Accounts accountsPagination = new Accounts();
accountsPagination.accounts = accounts; accountsPagination.accounts = accounts;
if (headers != null) {
accountsPagination.pagination = MastodonHelper.getPagination(headers); accountsPagination.pagination = MastodonHelper.getPagination(headers);
}
Runnable myRunnable = () -> accountsMutableLiveData.setValue(accountsPagination); Runnable myRunnable = () -> accountsMutableLiveData.setValue(accountsPagination);
mainHandler.post(myRunnable); mainHandler.post(myRunnable);
}).start(); }).start();
@ -487,7 +482,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -531,7 +526,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -577,7 +572,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -621,7 +616,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -665,7 +660,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -709,7 +704,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -753,7 +748,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -797,7 +792,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -841,7 +836,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();
@ -885,7 +880,7 @@ public class StatusesVM extends AndroidViewModel {
try { try {
Response<Status> statusResponse = statusCall.execute(); Response<Status> statusResponse = statusCall.execute();
if (statusResponse.isSuccessful()) { if (statusResponse.isSuccessful()) {
status = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusResponse.body()); status = statusResponse.body();
} else { } else {
if (statusResponse.errorBody() != null) { if (statusResponse.errorBody() != null) {
errorMessage = statusResponse.errorBody().string(); errorMessage = statusResponse.errorBody().string();

View file

@ -48,7 +48,6 @@ import app.fedilab.android.client.entities.peertube.PeertubeVideo;
import app.fedilab.android.exception.DBException; import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper; import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.TimelineHelper; import app.fedilab.android.helper.TimelineHelper;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import retrofit2.Call; import retrofit2.Call;
@ -121,7 +120,7 @@ public class TimelinesVM extends AndroidViewModel {
Response<List<Status>> publicTlResponse = publicTlCall.execute(); Response<List<Status>> publicTlResponse = publicTlCall.execute();
if (publicTlResponse.isSuccessful()) { if (publicTlResponse.isSuccessful()) {
statusList = publicTlResponse.body(); statusList = publicTlResponse.body();
statusList = SpannableHelper.convertStatus(getApplication().getApplicationContext(), statusList); statusList = statusList;
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -190,8 +189,7 @@ public class TimelinesVM extends AndroidViewModel {
Response<List<Status>> publicTlResponse = publicTlCall.execute(); Response<List<Status>> publicTlResponse = publicTlCall.execute();
if (publicTlResponse.isSuccessful()) { if (publicTlResponse.isSuccessful()) {
List<Status> notFilteredStatuses = publicTlResponse.body(); List<Status> notFilteredStatuses = publicTlResponse.body();
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.statuses = TimelineHelper.filterStatus(getApplication(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC);
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
statuses.pagination = MastodonHelper.getPagination(publicTlResponse.headers()); statuses.pagination = MastodonHelper.getPagination(publicTlResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -219,7 +217,6 @@ public class TimelinesVM extends AndroidViewModel {
statusesMutableLiveData = new MutableLiveData<>(); statusesMutableLiveData = new MutableLiveData<>();
new Thread(() -> { new Thread(() -> {
Call<Nitter> publicTlCall = mastodonTimelinesService.getNitter(accountsStr, max_position); Call<Nitter> publicTlCall = mastodonTimelinesService.getNitter(accountsStr, max_position);
Statuses statuses = new Statuses(); Statuses statuses = new Statuses();
if (publicTlCall != null) { if (publicTlCall != null) {
try { try {
@ -233,8 +230,10 @@ public class TimelinesVM extends AndroidViewModel {
statusList.add(status); statusList.add(status);
} }
} }
statuses.statuses = SpannableHelper.convertNitterStatus(getApplication().getApplicationContext(), statusList); statuses.statuses = 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) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -273,12 +272,11 @@ public class TimelinesVM extends AndroidViewModel {
List<Status> statusList = new ArrayList<>(); List<Status> statusList = new ArrayList<>();
if (misskeyNoteList != null) { if (misskeyNoteList != null) {
for (MisskeyNote misskeyNote : misskeyNoteList) { for (MisskeyNote misskeyNote : misskeyNoteList) {
Status status = MisskeyNote.convert(misskeyNote); Status status = MisskeyNote.convert(misskeyNote, instance);
statusList.add(status); statusList.add(status);
} }
} }
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.statuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC);
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
statuses.pagination = new Pagination(); statuses.pagination = new Pagination();
if (statusList.size() > 0) { if (statusList.size() > 0) {
statuses.pagination.min_id = statusList.get(0).id; statuses.pagination.min_id = statusList.get(0).id;
@ -324,8 +322,7 @@ public class TimelinesVM extends AndroidViewModel {
statusList.add(status); statusList.add(status);
} }
} }
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.statuses = TimelineHelper.filterStatus(getApplication(), statusList, TimelineHelper.FilterTimeLineType.PUBLIC);
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
statuses.pagination = new Pagination(); statuses.pagination = new Pagination();
if (statusList.size() > 0) { if (statusList.size() > 0) {
//These values are not used. //These values are not used.
@ -400,14 +397,14 @@ public class TimelinesVM extends AndroidViewModel {
MastodonTimelinesService mastodonTimelinesService = init(instance); MastodonTimelinesService mastodonTimelinesService = init(instance);
new Thread(() -> { new Thread(() -> {
Statuses statuses = new Statuses(); Statuses statuses = new Statuses();
Call<List<Status>> hashTagTlCall = mastodonTimelinesService.getHashTag(token, hashtag, local, onlyMedia, all, any, none, maxId, sinceId, minId, limit); String hashtagTrim = hashtag.replaceAll("\\#", "");
Call<List<Status>> hashTagTlCall = mastodonTimelinesService.getHashTag(token, hashtagTrim, local, onlyMedia, all, any, none, maxId, sinceId, minId, limit);
if (hashTagTlCall != null) { if (hashTagTlCall != null) {
try { try {
Response<List<Status>> hashTagTlResponse = hashTagTlCall.execute(); Response<List<Status>> hashTagTlResponse = hashTagTlCall.execute();
if (hashTagTlResponse.isSuccessful()) { if (hashTagTlResponse.isSuccessful()) {
List<Status> notFilteredStatuses = hashTagTlResponse.body(); List<Status> notFilteredStatuses = hashTagTlResponse.body();
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC); statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.PUBLIC);
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
statuses.pagination = MastodonHelper.getPagination(hashTagTlResponse.headers()); statuses.pagination = MastodonHelper.getPagination(hashTagTlResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -449,8 +446,7 @@ public class TimelinesVM extends AndroidViewModel {
Response<List<Status>> homeTlResponse = homeTlCall.execute(); Response<List<Status>> homeTlResponse = homeTlCall.execute();
if (homeTlResponse.isSuccessful()) { if (homeTlResponse.isSuccessful()) {
List<Status> notFilteredStatuses = homeTlResponse.body(); List<Status> notFilteredStatuses = homeTlResponse.body();
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME); statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), notFilteredStatuses, TimelineHelper.FilterTimeLineType.HOME);
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
statuses.pagination = MastodonHelper.getPagination(homeTlResponse.headers()); statuses.pagination = MastodonHelper.getPagination(homeTlResponse.headers());
if (!fetchingMissing) { if (!fetchingMissing) {
for (Status status : statuses.statuses) { for (Status status : statuses.statuses) {
@ -502,8 +498,7 @@ public class TimelinesVM extends AndroidViewModel {
statuses = statusCacheDAO.geStatuses(StatusCache.CacheEnum.HOME, instance, user_id, maxId, minId, sinceId); statuses = statusCacheDAO.geStatuses(StatusCache.CacheEnum.HOME, instance, user_id, maxId, minId, sinceId);
if (statuses != null) { if (statuses != null) {
List<Status> filteredStatuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME); statuses.statuses = TimelineHelper.filterStatus(getApplication().getApplicationContext(), statuses.statuses, TimelineHelper.FilterTimeLineType.HOME);
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), filteredStatuses);
if (statuses.statuses != null && statuses.statuses.size() > 0) { if (statuses.statuses != null && statuses.statuses.size() > 0) {
statuses.pagination = new Pagination(); statuses.pagination = new Pagination();
statuses.pagination.min_id = statuses.statuses.get(0).id; statuses.pagination.min_id = statuses.statuses.get(0).id;
@ -570,7 +565,7 @@ public class TimelinesVM extends AndroidViewModel {
try { try {
Response<List<Status>> listTlResponse = listTlCall.execute(); Response<List<Status>> listTlResponse = listTlCall.execute();
if (listTlResponse.isSuccessful()) { if (listTlResponse.isSuccessful()) {
statuses.statuses = SpannableHelper.convertStatus(getApplication().getApplicationContext(), listTlResponse.body()); statuses.statuses = listTlResponse.body();
statuses.pagination = MastodonHelper.getPagination(listTlResponse.headers()); statuses.pagination = MastodonHelper.getPagination(listTlResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {
@ -606,11 +601,6 @@ public class TimelinesVM extends AndroidViewModel {
Response<List<Conversation>> conversationsResponse = conversationsCall.execute(); Response<List<Conversation>> conversationsResponse = conversationsCall.execute();
if (conversationsResponse.isSuccessful()) { if (conversationsResponse.isSuccessful()) {
conversations.conversations = conversationsResponse.body(); 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()); conversations.pagination = MastodonHelper.getPagination(conversationsResponse.headers());
} }
} catch (Exception e) { } catch (Exception e) {

View file

@ -0,0 +1,101 @@
package app.fedilab.android.viewmodel.pleroma;
/* 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 <http://www.gnu.org/licenses>. */
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;
import java.util.List;
import java.util.concurrent.TimeUnit;
import app.fedilab.android.client.endpoints.PleromaAPI;
import app.fedilab.android.client.entities.api.Announcement;
import app.fedilab.android.helper.Helper;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ActionsVM extends AndroidViewModel {
final OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.callTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(getApplication().getApplicationContext()))
.build();
private MutableLiveData<Announcement> announcementMutableLiveData;
private MutableLiveData<List<Announcement>> announcementListMutableLiveData;
public ActionsVM(@NonNull Application application) {
super(application);
}
private PleromaAPI init(@NonNull String instance) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + instance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create(Helper.getDateBuilder()))
.client(okHttpClient)
.build();
return retrofit.create(PleromaAPI.class);
}
/**
* React to an announcement with an emoji.
*
* @param instance Instance domain of the active account
* @param token Access token of the active account
* @param id Local ID of an announcement
* @param name Unicode emoji, or shortcode of custom emoji
*/
public void addReaction(@NonNull String instance, String token, @NonNull String id, @NonNull String name) {
PleromaAPI pleromaAPI = init(instance);
new Thread(() -> {
Call<Void> addReactionCall = pleromaAPI.addReaction(token, id, name);
if (addReactionCall != null) {
try {
addReactionCall.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
/**
* Undo a react emoji to an announcement.
*
* @param instance Instance domain of the active account
* @param token Access token of the active account
* @param id Local ID of an announcement
* @param name Unicode emoji, or shortcode of custom emoji
*/
public void removeReaction(@NonNull String instance, String token, @NonNull String id, @NonNull String name) {
PleromaAPI pleromaAPI = init(instance);
new Thread(() -> {
Call<Void> removeReactionCall = pleromaAPI.removeReaction(token, id, name);
if (removeReactionCall != null) {
try {
removeReactionCall.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<shape />

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,17.5c2.33,0 4.31,-1.46 5.11,-3.5L6.89,14c0.8,2.04 2.78,3.5 5.11,3.5z" />
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z" />
</vector>

View file

@ -16,12 +16,27 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="24dp"> android:padding="24dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/set_account"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="12dp"
android:text="@string/account"
android:textAlignment="textStart"
android:textColor="@color/cyanea_accent_dark_reference"
app:icon="@drawable/ic_baseline_navigate_next_24"
app:iconGravity="end"
app:iconTint="@color/cyanea_accent_dark_reference"
app:strokeColor="@color/cyanea_accent_dark_reference" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/set_timelines" android:id="@+id/set_timelines"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingVertical="12dp" android:paddingVertical="12dp"
android:layout_marginTop="24dp"
android:text="@string/settings_category_label_timelines" android:text="@string/settings_category_label_timelines"
android:textAlignment="textStart" android:textAlignment="textStart"
android:textColor="@color/cyanea_accent_dark_reference" android:textColor="@color/cyanea_accent_dark_reference"

View file

@ -59,64 +59,9 @@
tools:maxLines="10" tools:maxLines="10"
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/lorem/random" />
<androidx.appcompat.widget.LinearLayoutCompat <include
android:id="@+id/status_reactions" android:id="@+id/layout_reactions"
android:layout_width="match_parent" layout="@layout/layout_reactions" />
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:paddingBottom="10dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/reactions_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/status_add_custom_emoji"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/add_reaction"
android:src="@drawable/ic_baseline_emoji_emotions_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/iconColor" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/status_emoji"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/add_reaction"
android:src="@drawable/ic_baseline_add_reaction_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/iconColor" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone">
<app.fedilab.android.helper.FedilabAutoCompleteTextView
android:id="@+id/fake_edittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAutofill="noExcludeDescendants"
android:inputType="text" />
</LinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>

View file

@ -24,7 +24,7 @@
android:layout_marginTop="@dimen/card_margin" android:layout_marginTop="@dimen/card_margin"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
app:cardElevation="2dp"> app:cardElevation="0dp">
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent" android:layout_width="match_parent"
@ -34,6 +34,9 @@
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp" />
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/status_booster_info" android:id="@+id/status_booster_info"
@ -77,10 +80,6 @@
tools:text="@tools:sample/full_names" /> tools:text="@tools:sample/full_names" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.divider.MaterialDivider
android:id="@+id/booster_divider"
android:layout_width="match_parent"
android:layout_height="1dp" />
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/header_container" android:id="@+id/header_container"
@ -151,12 +150,19 @@
android:maxLines="1" android:maxLines="1"
tools:text="@tools:sample/full_names" /> tools:text="@tools:sample/full_names" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/visibility_small"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="5dp"
android:src="@drawable/ic_baseline_public_24" />
<TextView <TextView
android:id="@+id/date_short" android:id="@+id/date_short"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginEnd="10dp" /> tools:text="2m" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
@ -196,9 +202,6 @@
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp" />
<androidx.appcompat.widget.AppCompatTextView <androidx.appcompat.widget.AppCompatTextView
android:id="@+id/spoiler" android:id="@+id/spoiler"
@ -492,6 +495,27 @@
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/action_share_container"
android:layout_width="match_parent"
android:layout_height="28dp"
android:layout_marginStart="48dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="6dp"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="end"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/action_share"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="28dp"
android:layout_height="28dp"
android:adjustViewBounds="true"
app:srcCompat="@drawable/ic_baseline_share_24" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/action_buttons" android:id="@+id/action_buttons"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -577,6 +601,12 @@
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
<include
android:id="@+id/layout_reactions"
layout="@layout/layout_reactions"
android:visibility="gone"
tools:visibility="visible" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>

View file

@ -19,12 +19,12 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/cardview_container"
android:layout_marginHorizontal="12dp" android:layout_marginHorizontal="12dp"
android:backgroundTint="?backgroundColorLight"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:clipChildren="false" android:clipChildren="false"
android:clipToPadding="false" android:clipToPadding="false"
app:cardElevation="2dp"> app:cardElevation="0dp">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -19,6 +19,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/cardview_container"
android:layout_marginHorizontal="12dp" android:layout_marginHorizontal="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:clipChildren="false" android:clipChildren="false"

View file

@ -86,6 +86,7 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/join_mastodon" android:id="@+id/join_mastodon"
style="@style/MyButtonColored"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"

View file

@ -151,6 +151,7 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/signup" android:id="@+id/signup"
style="@style/MyButtonColored"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/sign_up" /> android:text="@string/sign_up" />

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/status_reactions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:orientation="horizontal"
android:paddingBottom="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/reactions_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/status_add_custom_emoji"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/add_reaction"
android:src="@drawable/ic_baseline_emoji_emotions_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/iconColor" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/status_emoji"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginStart="10dp"
android:layout_marginEnd="5dp"
android:contentDescription="@string/add_reaction"
android:src="@drawable/ic_baseline_add_reaction_24"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/iconColor" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone">
<app.fedilab.android.helper.FedilabAutoCompleteTextView
android:id="@+id/fake_edittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAutofill="noExcludeDescendants"
android:inputType="text" />
</LinearLayout>
</androidx.appcompat.widget.LinearLayoutCompat>

View file

@ -45,8 +45,8 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:contentDescription="@string/profile_picture" android:contentDescription="@string/profile_picture"
android:paddingTop="@dimen/nav_header_vertical_spacing" android:paddingTop="@dimen/nav_header_vertical_spacing"
tools:src="@tools:sample/avatars" android:scaleType="fitCenter"
android:scaleType="fitCenter" /> tools:src="@tools:sample/avatars" />
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/change_account" android:id="@+id/change_account"

View file

@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:id="@+id/popup_container" android:layout_height="match_parent">
android:layout_height="match_parent"
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/popup_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
@ -415,7 +418,6 @@
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -446,5 +448,5 @@
android:layout_marginTop="10dp" /> android:layout_marginTop="10dp" />
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat> </androidx.appcompat.widget.LinearLayoutCompat>
</ScrollView>

View file

@ -11,6 +11,11 @@
android:icon="@drawable/ic_baseline_contact_page_24" android:icon="@drawable/ic_baseline_contact_page_24"
android:title="@string/contact" android:title="@string/contact"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/action_language"
android:icon="@drawable/ic_baseline_language_24"
android:title="@string/languages"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/action_microphone" android:id="@+id/action_microphone"
android:icon="@drawable/ic_baseline_mic_24" android:icon="@drawable/ic_baseline_mic_24"

View file

@ -720,4 +720,5 @@
<string name="messages_in_cache_for_other_timelines">Messàgios in memòria temporànea pro àteras lìnias de tempus</string> <string name="messages_in_cache_for_other_timelines">Messàgios in memòria temporànea pro àteras lìnias de tempus</string>
<string name="delete_cache_message">Ses seguru de bòlere isboidare sa memòria temporànea\? Si tenes abbotzos cun cuntenutos multimediales as a pèrdere sos cuntenutos alligados.</string> <string name="delete_cache_message">Ses seguru de bòlere isboidare sa memòria temporànea\? Si tenes abbotzos cun cuntenutos multimediales as a pèrdere sos cuntenutos alligados.</string>
<string name="clear_cache">Isbòida sa memòria temporànea</string> <string name="clear_cache">Isbòida sa memòria temporànea</string>
<string name="default_system_language">Imprea sa limba predefinida de sistema</string>
</resources> </resources>

View file

@ -726,4 +726,5 @@
<string name="clear_cache">Önbelleği temizle</string> <string name="clear_cache">Önbelleği temizle</string>
<string name="messages_in_cache_for_other_timelines">Diğer zaman çizelgeleri için önbellekteki mesajlar</string> <string name="messages_in_cache_for_other_timelines">Diğer zaman çizelgeleri için önbellekteki mesajlar</string>
<string name="messages_stored_in_drafts">Taslaklarda saklanan mesajlar</string> <string name="messages_stored_in_drafts">Taslaklarda saklanan mesajlar</string>
<string name="default_system_language">Öntanımlı sistem dilini kullan</string>
</resources> </resources>

View file

@ -16,7 +16,7 @@
<string name="download_from" formatted="false">Tập tin: %1$s</string> <string name="download_from" formatted="false">Tập tin: %1$s</string>
<string name="password">Mật khẩu</string> <string name="password">Mật khẩu</string>
<string name="email">Email</string> <string name="email">Email</string>
<string name="accounts">Tài khoản</string> <string name="accounts">Người</string>
<string name="toots">Tút</string> <string name="toots">Tút</string>
<string name="tags">Tag</string> <string name="tags">Tag</string>
<string name="save">Lưu</string> <string name="save">Lưu</string>
@ -63,7 +63,7 @@
<string name="no_emoji">Ứng dụng không thể thu thập emoji tùy chỉnh vào thời điểm này.</string> <string name="no_emoji">Ứng dụng không thể thu thập emoji tùy chỉnh vào thời điểm này.</string>
<string name="logout_account_confirmation">Bạn có chắc muốn đăng xuất @%1$s@%2$s\?</string> <string name="logout_account_confirmation">Bạn có chắc muốn đăng xuất @%1$s@%2$s\?</string>
<!-- Status --> <!-- Status -->
<string name="no_status">Không có tút nào</string> <string name="no_status">Chưa có</string>
<string name="favourite_add">Thêm tút này vào mục yêu thích của bạn\?</string> <string name="favourite_add">Thêm tút này vào mục yêu thích của bạn\?</string>
<string name="favourite_remove">Xóa tút này khỏi mục yêu thích của bạn\?</string> <string name="favourite_remove">Xóa tút này khỏi mục yêu thích của bạn\?</string>
<string name="reblog_add">Đăng lại tút này\?</string> <string name="reblog_add">Đăng lại tút này\?</string>
@ -75,7 +75,7 @@
<string name="more_action_5">Sao chép</string> <string name="more_action_5">Sao chép</string>
<string name="more_action_6">Chia sẻ</string> <string name="more_action_6">Chia sẻ</string>
<string name="more_action_7">Nhắc đến</string> <string name="more_action_7">Nhắc đến</string>
<string name="more_action_8">Thời gian ẩn</string> <string name="more_action_8">Ẩn có thời hạn</string>
<string name="more_action_9">Xóa &amp; viết lại</string> <string name="more_action_9">Xóa &amp; viết lại</string>
<string-array name="more_action_confirm"> <string-array name="more_action_confirm">
<item>Ẩn tài khoản này?</item> <item>Ẩn tài khoản này?</item>
@ -143,11 +143,14 @@
<string name="about_thekinrar">Tìm máy chủ:</string> <string name="about_thekinrar">Tìm máy chủ:</string>
<!-- Conversation --> <!-- Conversation -->
<!-- Accounts --> <!-- Accounts -->
<string name="no_accounts">Không có tài khoản nào</string> <string name="no_accounts">Không có người nào</string>
<string name="no_follow_request">Không có yêu cầu theo dõi</string> <string name="no_follow_request">Không có yêu cầu theo dõi</string>
<string name="status_cnt">%1$s tút</string> <string name="status_cnt">Tút
<string name="following_cnt">%1$s Đang theo dõi</string> \n %1$s</string>
<string name="followers_cnt">%1$s Người theo dõi</string> <string name="following_cnt">Đang theo dõi
\n %1$s</string>
<string name="followers_cnt">Người theo dõi
\n %1$s</string>
<string name="reject">Từ chối</string> <string name="reject">Từ chối</string>
<!-- Scheduled toots --> <!-- Scheduled toots -->
<string name="no_scheduled_toots">Không có tút đã lên lịch!</string> <string name="no_scheduled_toots">Không có tút đã lên lịch!</string>
@ -198,10 +201,10 @@
<string name="set_notif_follow_share">Khi ai đó đăng lại tút của tôi</string> <string name="set_notif_follow_share">Khi ai đó đăng lại tút của tôi</string>
<string name="set_notif_follow_add">Khi ai đó thích tút của tôi</string> <string name="set_notif_follow_add">Khi ai đó thích tút của tôi</string>
<string name="set_notif_follow_mention">Khi ai đó nhắc tới tôi</string> <string name="set_notif_follow_mention">Khi ai đó nhắc tới tôi</string>
<string name="set_notif_follow_poll">Nhắc khi một cuộc bình chọn kết thúc</string> <string name="set_notif_follow_poll">Khi cuộc bình chọn kết thúc</string>
<string name="set_notif_status">Nhắc khi có tút mới</string> <string name="set_notif_status">Khi có tút mới</string>
<string name="set_share_validation">Hiện xác nhận trước khi đăng lại tút</string> <string name="set_share_validation">Hiện xác nhận trước khi đăng lại</string>
<string name="set_share_validation_fav">Hiện xác nhận trước khi thích tút</string> <string name="set_share_validation_fav">Hiện xác nhận trước khi thích</string>
<string name="set_notify">Thông báo?</string> <string name="set_notify">Thông báo?</string>
<string name="set_notif_silent">Tắt thông báo</string> <string name="set_notif_silent">Tắt thông báo</string>
<string name="set_nsfw_timeout">Thời gian chờ của NSFW (giây, 0 = tắt)</string> <string name="set_nsfw_timeout">Thời gian chờ của NSFW (giây, 0 = tắt)</string>
@ -237,7 +240,7 @@
<string name="action_unmute">Bỏ ẩn</string> <string name="action_unmute">Bỏ ẩn</string>
<string name="request_sent">Đã gửi yêu cầu</string> <string name="request_sent">Đã gửi yêu cầu</string>
<string name="followed_by">Theo dõi bạn</string> <string name="followed_by">Theo dõi bạn</string>
<string name="set_capitalize">Viết hoa chữ đầu khi trả lời</string> <string name="set_capitalize">Tự động xuống dòng khi trả lời</string>
<string name="set_resize_picture">Giảm kích cỡ hình ảnh</string> <string name="set_resize_picture">Giảm kích cỡ hình ảnh</string>
<string name="set_resize_video">Giảm kích thước video</string> <string name="set_resize_video">Giảm kích thước video</string>
<!-- Quick settings for notifications --> <!-- Quick settings for notifications -->
@ -273,7 +276,7 @@
<string name="action_lists_add_to">Thêm vào danh sách</string> <string name="action_lists_add_to">Thêm vào danh sách</string>
<string name="action_lists_delete">Xoá danh sách</string> <string name="action_lists_delete">Xoá danh sách</string>
<string name="action_lists_title_placeholder">Tên danh sách mới</string> <string name="action_lists_title_placeholder">Tên danh sách mới</string>
<string name="action_lists_add_user">Đã thêm tài khoản vào danh sách!</string> <string name="action_lists_add_user">Đã thêm người này vào danh sách!</string>
<string name="action_lists_empty">Bạn chưa có danh sách nào!</string> <string name="action_lists_empty">Bạn chưa có danh sách nào!</string>
<!-- Migration --> <!-- Migration -->
<string name="account_moved_to">%1$s đã chuyển sang %2$s</string> <string name="account_moved_to">%1$s đã chuyển sang %2$s</string>
@ -325,7 +328,7 @@
<string name="channel_notif_status">Tút mới</string> <string name="channel_notif_status">Tút mới</string>
<string name="channel_notif_media">Tải media</string> <string name="channel_notif_media">Tải media</string>
<string name="select_sound">Chọn âm thanh</string> <string name="select_sound">Chọn âm thanh</string>
<string name="set_enable_time_slot">Hiện thời gian đăng</string> <string name="set_enable_time_slot">Thời gian thông báo</string>
<string name="block_domain_confirm_message">Bạn có chắc bỏ chặn %s\? <string name="block_domain_confirm_message">Bạn có chắc bỏ chặn %s\?
\n \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.</string> \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.</string>
@ -405,9 +408,9 @@
<string name="poll_choice_s">Lựa chọn %d</string> <string name="poll_choice_s">Lựa chọn %d</string>
<string name="poll_invalid_choices">Bạn cần cho ít nhất 2 lựa chọn!</string> <string name="poll_invalid_choices">Bạn cần cho ít nhất 2 lựa chọn!</string>
<string name="done">Xong</string> <string name="done">Xong</string>
<string name="poll_finish_at">kết thúc lúc %s</string> <string name="poll_finish_at">kết thúc %s</string>
<string name="vote">Bình chọn</string> <string name="vote">Bình chọn</string>
<string name="notif_poll">Cuộc bình chọn bạn tham gia đã kết thúc</string> <string name="notif_poll">Cuộc bình chọn bạn đã tham gia kết thúc</string>
<string name="notif_poll_self">Cuộc bình chọn của bạn đã kết thúc</string> <string name="notif_poll_self">Cuộc bình chọn của bạn đã kết thúc</string>
<string name="settings_category_notif_categories">Loại</string> <string name="settings_category_notif_categories">Loại</string>
<string name="move_timeline">Chuyển bảng tin</string> <string name="move_timeline">Chuyển bảng tin</string>
@ -490,7 +493,7 @@
<string name="voice_message">Ghi âm</string> <string name="voice_message">Ghi âm</string>
<string name="set_enable_time_slot_indication">Ứ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.</string> <string name="set_enable_time_slot_indication">Ứ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.</string>
<string name="set_fit_preview_indication">Không cắt ảnh xem trước</string> <string name="set_fit_preview_indication">Không cắt ảnh xem trước</string>
<string name="set_capitalize_indication">Tự động chèn dấu ngắt dòng sau phần đề cập để viết hoa chữ cái đầu tiên</string> <string name="set_capitalize_indication">Tự động xuống dòng và viết hoa chữ đầu tiên sau khi nhắc đến ai đó.</string>
<string name="settings_title_custom_sharing_indication">Chia sẻ tút với nguồn cấp dữ liệu RSS</string> <string name="settings_title_custom_sharing_indication">Chia sẻ tút với nguồn cấp dữ liệu RSS</string>
<string name="compose">Soạn thảo</string> <string name="compose">Soạn thảo</string>
<string name="select">Chọn</string> <string name="select">Chọn</string>
@ -502,8 +505,8 @@
\n \n
\nBạn có thể thêm nội dung. Cảm ơn bạn!</string> \nBạn có thể thêm nội dung. Cảm ơn bạn!</string>
<string name="visibility">Mức độ hiển thị</string> <string name="visibility">Mức độ hiển thị</string>
<string name="set_disable_animated_emoji">Tắt emoji dạng GIF</string> <string name="set_disable_animated_emoji">Tắt emoji GIF</string>
<string name="report_account">Báo cáo tài khoản</string> <string name="report_account">Báo cáo người này</string>
<plurals name="number_of_voters"> <plurals name="number_of_voters">
<item quantity="other">%d bình chọn</item> <item quantity="other">%d bình chọn</item>
</plurals> </plurals>
@ -527,10 +530,10 @@
<string name="poll_duplicated_entry">Có lựa chọn trùng lặp!</string> <string name="poll_duplicated_entry">Có lựa chọn trùng lặp!</string>
<string name="set_clear_cache_exit">Xóa bộ nhớ đệm khi thoát</string> <string name="set_clear_cache_exit">Xóa bộ nhớ đệm khi thoát</string>
<string name="set_clear_cache_exit_indication">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.</string> <string name="set_clear_cache_exit_indication">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.</string>
<string name="unfollow_confirm">Bạn có muốn ngưng theo dõi tài khoản này\?</string> <string name="unfollow_confirm">Bạn có muốn ngưng theo dõi người này\?</string>
<string name="set_unfollow_validation">Yêu cầu xác nhận trước khi hủy theo dõi ai đó</string> <string name="set_unfollow_validation">Yêu cầu xác nhận trước khi ngưng theo dõi ai đó.</string>
<string name="replace_medium">Thay thế liên kết từ Medium</string> <string name="replace_medium">Thay thế liên kết từ Medium</string>
<string name="replace_medium_description">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.</string> <string name="replace_medium_description">Dùng một frontend thay thế cho Medium</string>
<string name="replace_medium_host">Mặc định: scribe.rip</string> <string name="replace_medium_host">Mặc định: scribe.rip</string>
<string name="set_push_notifications">Sử dụng hệ thống thông báo đẩy để nhận thông báo trong thời gian thực.</string> <string name="set_push_notifications">Sử dụng hệ thống thông báo đẩy để nhận thông báo trong thời gian thực.</string>
<string name="action_add_notes">Thêm ghi chú</string> <string name="action_add_notes">Thêm ghi chú</string>
@ -615,10 +618,10 @@
<string name="not_valid_list_name">Tên danh sách không hợp lệ!</string> <string name="not_valid_list_name">Tên danh sách không hợp lệ!</string>
<string name="no_account_in_list">Không có người dùng nào trong danh sách này!</string> <string name="no_account_in_list">Không có người dùng nào trong danh sách này!</string>
<string name="scheduled">Đã lên lịch</string> <string name="scheduled">Đã lên lịch</string>
<string name="notifications_are">Trong khoảng thời gian</string> <string name="notifications_are">Trong khoảng thời gian này</string>
<string name="pref_theme_base">Theme gốc</string> <string name="pref_theme_base">Theme gốc</string>
<string name="pref_theme_base_summary">Chọn theme gốc là tối hay sáng</string> <string name="pref_theme_base_summary">Chọn theme gốc là tối hay sáng</string>
<string name="customize_timelines">Tùy chỉnh bảng tin</string> <string name="customize_timelines">Tùy chỉnh</string>
<string name="pref_contributor">Theme của cộng đồng</string> <string name="pref_contributor">Theme của cộng đồng</string>
<string name="pref_contributor_summary">Chọn một theme được tạo bởi cộng đồng</string> <string name="pref_contributor_summary">Chọn một theme được tạo bởi cộng đồng</string>
<string name="display">Hiển thị</string> <string name="display">Hiển thị</string>
@ -644,7 +647,7 @@
<string name="toots_visibility_title">Kiểu tút mặc định:</string> <string name="toots_visibility_title">Kiểu tút mặc định:</string>
<string name="toast_bookmark">Đã thêm tút vào mục yêu thích!</string> <string name="toast_bookmark">Đã thêm tút vào mục yêu thích!</string>
<string name="toast_unbookmark">Đã xóa tút khỏi mục yêu thích!</string> <string name="toast_unbookmark">Đã xóa tút khỏi mục yêu thích!</string>
<string name="set_accounts_page">Số lượng tài khoản mỗi lần tải</string> <string name="set_accounts_page">Số lượng người dùng mỗi lần tải</string>
<string name="set_notifications_page">Số lượng thông báo mỗi lần tải</string> <string name="set_notifications_page">Số lượng thông báo mỗi lần tải</string>
<string name="category_music">Âm nhạc</string> <string name="category_music">Âm nhạc</string>
<string name="cannot_be_empty">Không thể để trống mục này!</string> <string name="cannot_be_empty">Không thể để trống mục này!</string>
@ -665,7 +668,7 @@
<string name="instance_not_valid">Địa chỉ máy chủ không hợp lệ!</string> <string name="instance_not_valid">Địa chỉ máy chủ không hợp lệ!</string>
<string name="boosted_by">Đăng lại bởi</string> <string name="boosted_by">Đăng lại bởi</string>
<string name="favourited_by">Thích bởi</string> <string name="favourited_by">Thích bởi</string>
<string name="followers_only">Hạn chế</string> <string name="followers_only">Riêng tư</string>
<string name="other">Khác</string> <string name="other">Khác</string>
<string name="eg_sensitive_content">Vd: Nội Dung Nhạy Cảm</string> <string name="eg_sensitive_content">Vd: Nội Dung Nhạy Cảm</string>
<string name="add_status">Thêm trạng thái</string> <string name="add_status">Thêm trạng thái</string>
@ -688,11 +691,11 @@
<string name="report_val2">Đây là spam</string> <string name="report_val2">Đây là spam</string>
<string name="report_val_more2">Liên kết độc hại, giả tương tác hoặc trả lời lặp đi lặp lại</string> <string name="report_val_more2">Liên kết độc hại, giả tương tác hoặc trả lời lặp đi lặp lại</string>
<string name="report_val3">Vi phạm quy tắc máy chủ</string> <string name="report_val3">Vi phạm quy tắc máy chủ</string>
<string name="report_1_unfollow">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ọ.</string> <string name="report_1_unfollow">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ọ.</string>
<string name="report_1_mute_title">Ẩn %1$s</string> <string name="report_1_mute_title">Ẩn %1$s</string>
<string name="report_1_mute">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.</string> <string name="report_1_mute">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.</string>
<string name="report_1_block_title">Chặn %1$s</string> <string name="report_1_block_title">Chặn %1$s</string>
<string name="report_more_remote">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ủ đó\?</string> <string name="report_more_remote">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ủ đó\?</string>
<string name="report_more_forward">Chuyển tiếp %1$s</string> <string name="report_more_forward">Chuyển tiếp %1$s</string>
<string name="report_sent">Đã gửi báo cáo!</string> <string name="report_sent">Đã gửi báo cáo!</string>
<string name="report_1_block">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.</string> <string name="report_1_block">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.</string>
@ -704,7 +707,7 @@
<string name="notif_display_poll_results">Kết quả bình chọn</string> <string name="notif_display_poll_results">Kết quả bình chọn</string>
<string name="notif_display_updates_from_people">Cập nhật từ mọi người</string> <string name="notif_display_updates_from_people">Cập nhật từ mọi người</string>
<string name="notif_display_follows">Theo dõi</string> <string name="notif_display_follows">Theo dõi</string>
<string name="mark_all_as_read">Đánh dấu tất cả là đã đọc</string> <string name="mark_all_as_read">Đánh dấu đã đọc xong</string>
<string name="display_all_categories">Hiện toàn bộ</string> <string name="display_all_categories">Hiện toàn bộ</string>
<string name="delete_notification_all_warning">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.</string> <string name="delete_notification_all_warning">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.</string>
<string name="about_mastodon">\"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.\"</string> <string name="about_mastodon">\"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.\"</string>
@ -730,7 +733,7 @@
<string name="most_recent">Gần đây nhất</string> <string name="most_recent">Gần đây nhất</string>
<string name="filter">Bộ lọc</string> <string name="filter">Bộ lọc</string>
<string name="domain">Tên miền</string> <string name="domain">Tên miền</string>
<string name="origin_report">Nguồn gốc tài khoản bị báo cáo</string> <string name="origin_report">Máy chủ người bị báo cáo</string>
<string name="status">Trạng thái</string> <string name="status">Trạng thái</string>
<string name="resolved">Đã xử lý</string> <string name="resolved">Đã xử lý</string>
<string name="approved">Đã duyệt</string> <string name="approved">Đã duyệt</string>
@ -745,10 +748,11 @@
<string name="label_line">Đường thẳng</string> <string name="label_line">Đường thẳng</string>
<string name="label_eraser_mode">Chế độ Xóa</string> <string name="label_eraser_mode">Chế độ Xóa</string>
<string name="delete_cache">Xóa cache</string> <string name="delete_cache">Xóa cache</string>
<string name="messages_in_cache_for_home">Tút trong cache cho Bảng Tin</string> <string name="messages_in_cache_for_home">Bộ nhớ đệm tút Bảng Tin</string>
<string name="messages_stored_in_drafts">Tút lưu trong nháp</string> <string name="messages_stored_in_drafts">Tút nháp</string>
<string name="delete_cache_message">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.</string> <string name="delete_cache_message">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.</string>
<string name="messages_in_cache_for_other_timelines">Tút trong cache những Bảng Tin khác</string> <string name="messages_in_cache_for_other_timelines">Bộ nhớ đệm tút những Bảng Tin khác</string>
<string name="files_cache_size">Kích cỡ cache</string> <string name="files_cache_size">Dung lượng cho phép</string>
<string name="clear_cache">Xóa cache</string> <string name="clear_cache">Xóa bộ nhớ đệm</string>
<string name="default_system_language">Dùng ngôn ngữ hệ thống</string>
</resources> </resources>

View file

@ -779,6 +779,7 @@
<string name="SET_FEATURED_TAG_ACTION" translatable="false">SET_FEATURED_TAG_ACTION</string> <string name="SET_FEATURED_TAG_ACTION" translatable="false">SET_FEATURED_TAG_ACTION</string>
<string name="SET_RETRIEVE_METADATA_IF_URL_FROM_EXTERAL" translatable="false">SET_RETRIEVE_METADATA_IF_URL_FROM_EXTERAL</string> <string name="SET_RETRIEVE_METADATA_IF_URL_FROM_EXTERAL" translatable="false">SET_RETRIEVE_METADATA_IF_URL_FROM_EXTERAL</string>
<string name="SET_TRANSLATE_VALUES_RESET" translatable="false">SET_TRANSLATE_VALUES_RESET</string>
<string-array name="SET_TRANSLATE_ENTRIES" translatable="false"> <string-array name="SET_TRANSLATE_ENTRIES" translatable="false">
<item>en</item> <item>en</item>
<item>fr</item> <item>fr</item>
@ -863,6 +864,7 @@
<string name="SET_TIME_FROM" translatable="false">SET_TIME_FROM</string> <string name="SET_TIME_FROM" translatable="false">SET_TIME_FROM</string>
<string name="SET_TIME_TO" translatable="false">SET_TIME_TO</string> <string name="SET_TIME_TO" translatable="false">SET_TIME_TO</string>
<string name="SET_MED_DESC_TIMEOUT" translatable="false">SET_MED_DESC_TIMEOUT</string> <string name="SET_MED_DESC_TIMEOUT" translatable="false">SET_MED_DESC_TIMEOUT</string>
<string name="SET_COMPOSE_LANGUAGE" translatable="false">SET_COMPOSE_LANGUAGE</string>
<string name="SET_NOTIF_FOLLOW" translatable="false">SET_NOTIF_FOLLOW</string> <string name="SET_NOTIF_FOLLOW" translatable="false">SET_NOTIF_FOLLOW</string>
@ -894,7 +896,7 @@
<string name="SET_ACCOUNTS_PER_CALL" translatable="false">SET_ACCOUNTS_PER_CALL</string> <string name="SET_ACCOUNTS_PER_CALL" translatable="false">SET_ACCOUNTS_PER_CALL</string>
<string name="SET_STATUSES_PER_CALL" translatable="false">SET_STATUSES_PER_CALL</string> <string name="SET_STATUSES_PER_CALL" translatable="false">SET_STATUSES_PER_CALL</string>
<string name="SET_NOTIFICATIONS_PER_CALL" translatable="false">SET_NOTIFICATIONS_PER_CALL</string> <string name="SET_NOTIFICATIONS_PER_CALL" translatable="false">SET_NOTIFICATIONS_PER_CALL</string>
<string name="INSTANCE_INFO" translatable="false">INSTANCE_INFO</string>
<string name="SET_INVIDIOUS" translatable="false">SET_INVIDIOUS</string> <string name="SET_INVIDIOUS" translatable="false">SET_INVIDIOUS</string>
<string name="SET_INVIDIOUS_HOST" translatable="false">SET_INVIDIOUS_HOST</string> <string name="SET_INVIDIOUS_HOST" translatable="false">SET_INVIDIOUS_HOST</string>
<string name="DEFAULT_INVIDIOUS_HOST" translatable="false">invidious.snopyta.org</string> <string name="DEFAULT_INVIDIOUS_HOST" translatable="false">invidious.snopyta.org</string>
@ -978,6 +980,8 @@
<string name="files_cache_size">File cache size</string> <string name="files_cache_size">File cache size</string>
<string name="clear_cache">Clear cache</string> <string name="clear_cache">Clear cache</string>
<string name="delete_cache_message">Are you sure you want to delete cache? If you have drafts with media, the attached media will be lost.</string> <string name="delete_cache_message">Are you sure you want to delete cache? If you have drafts with media, the attached media will be lost.</string>
<string name="default_system_language">Use the default system language</string>
<string name="message_language">Language for messages</string>
<string-array name="photo_editor_emoji" translatable="false"> <string-array name="photo_editor_emoji" translatable="false">
<!-- Smiles --> <!-- Smiles -->

View file

@ -187,7 +187,7 @@
<item name="iconTint">@color/cyanea_accent_dark_reference</item> <item name="iconTint">@color/cyanea_accent_dark_reference</item>
<item name="strokeColor">@color/cyanea_accent_dark_reference</item> <item name="strokeColor">@color/cyanea_accent_dark_reference</item>
<item name="rippleColor">@color/cyanea_accent_dark_reference</item> <item name="rippleColor">@color/cyanea_accent_dark_reference</item>
<item name="android:backgroundTint">@color/cyanea_accent_dark_reference</item> <item name="backgroundTint">@color/cyanea_accent_dark_reference</item>
</style> </style>
<style name="MyOutlinedButton" parent="Widget.MaterialComponents.Button.OutlinedButton"> <style name="MyOutlinedButton" parent="Widget.MaterialComponents.Button.OutlinedButton">

View file

@ -11,4 +11,10 @@
app:summary="@string/set_push_notifications" app:summary="@string/set_push_notifications"
app:title="@string/set_change_locale" app:title="@string/set_change_locale"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<Preference
android:key="@string/SET_TRANSLATE_VALUES_RESET"
android:summary="@string/default_system_language"
android:title="@string/reset"
app:iconSpaceReserved="false" />
</PreferenceScreen> </PreferenceScreen>

View file

@ -0,0 +1 @@
- Fix some bugs reported.

View file

@ -0,0 +1,6 @@
- Keep improving the scroll behaviour
- Scroll to top (tab reselection) will fetch new messages and then scroll to top
- Remove focus point for fit media preview
- Fix cannot share with one account
- Fix black theme
- Fix some button colors

View file

@ -0,0 +1,16 @@
Added:
- Set compose language (from compose menu -> three vertical dots)
- Add reactions support for Pleroma
- Add privacy indicator at the top right
Changed
- Improve the scrolling behaviour
- Scroll to top (tab reselection) will fetch new messages and then scroll to top
Fixed:
- Empty tag timelines
- Remove focus point for fit media preview
- Fix cannot share with one account
- Fix black theme
- Theme cannot be selected
- Fix some button colors