From e897b3af57c8f7e5516878a016537f8026cc26a1 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 2 Jun 2023 19:23:23 +0200 Subject: [PATCH 01/79] New translations strings.xml (German) --- mastodon/src/main/res/values-de-rDE/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastodon/src/main/res/values-de-rDE/strings.xml b/mastodon/src/main/res/values-de-rDE/strings.xml index 1c1032192..d06d55d62 100644 --- a/mastodon/src/main/res/values-de-rDE/strings.xml +++ b/mastodon/src/main/res/values-de-rDE/strings.xml @@ -200,7 +200,7 @@ Überprüfe deinen Posteingang Klicke auf den Link, den wir dir geschickt haben, um %s zu bestätigen. Wir warten hier auf dich. - Kein Link erhalten? + Keinen Link erhalten? Erneut abschicken E-Mail-App öffnen Bestätigung per E-Mail zugeschickt From d7f73e02c5bbc0a8deddd5941e69a4e3516947a2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 2 Jun 2023 20:22:32 +0200 Subject: [PATCH 02/79] New translations strings.xml (German) --- mastodon/src/main/res/values-de-rDE/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/src/main/res/values-de-rDE/strings.xml b/mastodon/src/main/res/values-de-rDE/strings.xml index d06d55d62..78979a045 100644 --- a/mastodon/src/main/res/values-de-rDE/strings.xml +++ b/mastodon/src/main/res/values-de-rDE/strings.xml @@ -166,7 +166,7 @@ Während wir den Vorfall überprüfen, kannst du gegen %s weitere Maßnahmen ergreifen. %s entfolgen Entfolgen - Du wirst die eigenen und geteilten Beiträge des Kontos nicht mehr sehen können. Dass du das Profil stummgeschaltet hast, erfährt die Person nicht. + Du wirst deren (geteilte) Beiträge auf deiner Startseite nicht mehr sehen können. Sie werden nicht erfahren, dass sie stummgeschaltet sind. Dir wird es nicht länger möglich sein, die Beiträge dieses Konto zu sehen. Das blockierte Profil wird nicht mehr in der Lage sein, deine Beiträge zu sehen oder dir zu folgen. Die Person hinter dem Konto wird mitbekommen, dass du ihr Konto gesperrt hast. Möchtest du das nicht mehr sehen? Wenn du etwas auf Mastodon siehst, das dir nicht gefällt, kannst du die Person aus deinem Umfeld entfernen. @@ -175,7 +175,7 @@ Wähle einen Server basierend auf deinen Interessen oder deiner Region – oder einfach einen allgemeinen. Du kannst trotzdem mit jedem interagieren, egal auf welchem Server. Servername oder -adresse Server-Regeln - Mit dem Fortfahren erklärst du dich damit einverstanden, die folgenden Regeln zu befolgen, die von den %s-Moderatoren aufgestellt und umgesetzt werden. + Solltest du fortfahren, erklärst du dich mit den Serverregeln, die die Moderator*innen von %s aufgestellt haben und durchsetzen werden, einverstanden. Konto erstellen bearbeiten Name From 995f47870895cc2ed0ce6eca1b471e3932b62be5 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 20:31:00 +0200 Subject: [PATCH 03/79] allow sharing @-handles with megalodon closes sk22#540 --- .../android/ui/utils/UiUtilsTest.java | 101 ++++++++++++++++++ .../android/ExternalShareActivity.java | 29 +++-- .../android/ui/AccountSwitcherSheet.java | 10 +- .../android/ui/utils/UiUtils.java | 77 ++++++++++++- mastodon/src/main/res/values/strings_sk.xml | 1 + 5 files changed, 203 insertions(+), 15 deletions(-) create mode 100644 mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java diff --git a/mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java b/mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java new file mode 100644 index 000000000..2fd84eb77 --- /dev/null +++ b/mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java @@ -0,0 +1,101 @@ +package org.joinmastodon.android.ui.utils; + +import static org.junit.Assert.*; + +import android.util.Pair; + +import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Instance; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Optional; + +public class UiUtilsTest { + @BeforeClass + public static void createDummySession() { + Instance dummyInstance = new Instance(); + dummyInstance.uri = "test.tld"; + Account dummyAccount = new Account(); + dummyAccount.id = "123456"; + AccountSessionManager.getInstance().addAccount(dummyInstance, null, dummyAccount, null, null); + } + + @AfterClass + public static void cleanUp() { + AccountSessionManager.getInstance().removeAccount("test.tld_123456"); + } + + @Test + public void looksLikeFediverseHandle() { + assertEquals( + Optional.of(Pair.create("megalodon", Optional.of("floss.social"))), + UiUtils.looksLikeFediverseHandle("megalodon@floss.social") + ); + + assertEquals( + Optional.of(Pair.create("megalodon", Optional.of("floss.social"))), + UiUtils.looksLikeFediverseHandle("@megalodon@floss.social") + ); + + assertEquals( + Optional.of(Pair.create("megalodon", Optional.empty())), + UiUtils.looksLikeFediverseHandle("@megalodon") + ); + + assertEquals( + Optional.empty(), + UiUtils.looksLikeFediverseHandle("megalodon") + ); + + assertEquals( + Optional.empty(), + UiUtils.looksLikeFediverseHandle("this is not a fedi handle") + ); + + assertEquals( + Optional.empty(), + UiUtils.looksLikeFediverseHandle("not@a-domain") + ); + } + + @Test + public void acctMatches() { + assertTrue("local account, domain not specified", UiUtils.acctMatches( + "test.tld_123456", + "someone", + "someone", + null + )); + + assertTrue("domain not specified", UiUtils.acctMatches( + "test.tld_123456", + "someone@somewhere.social", + "someone", + null + )); + + assertTrue("local account, domain specified, different casing", UiUtils.acctMatches( + "test.tld_123456", + "SomeOne", + "someone", + "Test.TLD" + )); + + assertFalse("username doesn't match", UiUtils.acctMatches( + "test.tld_123456", + "someone-else@somewhere.social", + "someone", + "somewhere.social" + )); + + assertFalse("domain doesn't match", UiUtils.acctMatches( + "test.tld_123456", + "someone@somewhere.social", + "someone", + "somewhere.else" + )); + } +} \ No newline at end of file diff --git a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java index be1657660..d1ad5468b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; +import android.util.Pair; import android.widget.Toast; import org.joinmastodon.android.api.session.AccountSession; @@ -19,6 +20,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.BiConsumer; import androidx.annotation.Nullable; import me.grishka.appkit.FragmentStackActivity; @@ -31,19 +33,23 @@ public class ExternalShareActivity extends FragmentStackActivity{ if(savedInstanceState==null){ Optional text = Optional.ofNullable(getIntent().getStringExtra(Intent.EXTRA_TEXT)); - boolean isMastodonURL = text.map(UiUtils::looksLikeMastodonUrl).orElse(false); + Optional>> fediHandle = text.flatMap(UiUtils::looksLikeFediverseHandle); + boolean isFediUrl = text.map(UiUtils::looksLikeFediverseUrl).orElse(false); + boolean isOpenable = isFediUrl || fediHandle.isPresent(); List sessions=AccountSessionManager.getInstance().getLoggedInAccounts(); - if(sessions.isEmpty()){ + if (sessions.isEmpty()){ Toast.makeText(this, R.string.err_not_logged_in, Toast.LENGTH_SHORT).show(); finish(); - }else if(sessions.size()==1 && !isMastodonURL){ - openComposeFragment(sessions.get(0).getID()); - }else{ - new AccountSwitcherSheet(this, null, true, isMastodonURL, (accountId, open) -> { + } else if (isOpenable || sessions.size() > 1) { + AccountSwitcherSheet sheet = new AccountSwitcherSheet(this, null, true, isOpenable); + if (isOpenable) sheet.setOnClick((accountId, open) -> { if (open && text.isPresent()) { - UiUtils.lookupURL(this, accountId, text.get(), false, (clazz, args) -> { + BiConsumer, Bundle> callback = (clazz, args) -> { if (clazz == null) { + Toast.makeText(this, R.string.sk_open_in_app_failed, Toast.LENGTH_SHORT).show(); + // TODO: do something about the window getting leaked + sheet.dismiss(); finish(); return; } @@ -52,11 +58,16 @@ public class ExternalShareActivity extends FragmentStackActivity{ intent.putExtras(args); finish(); startActivity(intent); - }); + }; + if (isFediUrl) UiUtils.lookupURL(this, accountId, text.get(), false, callback); + else UiUtils.lookupAccountHandle(this, accountId, fediHandle.get(), callback); } else { openComposeFragment(accountId); } - }).show(); + }); + sheet.show(); + } else if (sessions.size() == 1) { + openComposeFragment(sessions.get(0).getID()); } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java index 0eb9f205d..e6cc268ab 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java @@ -58,18 +58,18 @@ import me.grishka.appkit.views.UsableRecyclerView; public class AccountSwitcherSheet extends BottomSheet{ private final Activity activity; private final HomeFragment fragment; - private final BiConsumer onClick; private final boolean externalShare, openInApp; + private BiConsumer onClick; private UsableRecyclerView list; private List accounts; private ListImageLoaderWrapper imgLoader; private AccountsAdapter accountsAdapter; public AccountSwitcherSheet(@NonNull Activity activity, @Nullable HomeFragment fragment){ - this(activity, fragment, false, false, null); + this(activity, fragment, false, false); } - public AccountSwitcherSheet(@NonNull Activity activity, @Nullable HomeFragment fragment, boolean externalShare, boolean openInApp, BiConsumer onClick){ + public AccountSwitcherSheet(@NonNull Activity activity, @Nullable HomeFragment fragment, boolean externalShare, boolean openInApp){ super(activity); this.activity=activity; this.fragment=fragment; @@ -123,6 +123,10 @@ public class AccountSwitcherSheet extends BottomSheet{ UiUtils.getThemeColor(activity, R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme()); } + public void setOnClick(BiConsumer onClick) { + this.onClick = onClick; + } + private void confirmLogOut(String accountID){ AccountSession session=AccountSessionManager.getInstance().getAccount(accountID); new M3AlertDialogBuilder(activity) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index f2b3f1287..01bc646bd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -37,6 +37,7 @@ import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.view.HapticFeedbackConstants; import android.view.Menu; import android.view.MenuItem; @@ -98,9 +99,9 @@ import org.parceler.Parcels; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.net.IDN; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -905,6 +906,29 @@ public class UiUtils { return theme == GlobalUserPreferences.ThemePreference.DARK; } + public static Optional>> looksLikeFediverseHandle(String maybeFediHandle) { + // https://stackoverflow.com/a/26987741, except i put a + here ... v + String domainRegex = "^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]\\.)+(xn--)?([a-z0-9][a-z0-9\\-]{0,60}|[a-z0-9-]{1,30}\\.[a-z]{2,})$"; + try { + List parts = Arrays.stream(maybeFediHandle.split("@")) + .filter(part -> !part.isEmpty()) + .collect(Collectors.toList()); + if (parts.size() == 0 || !parts.get(0).matches("^[^/\\s]+$")) { + return Optional.empty(); + } else if (parts.size() == 2) { + String domain = IDN.toASCII(parts.get(1)); + if (!domain.matches(domainRegex)) return Optional.empty(); + return Optional.of(Pair.create(parts.get(0), Optional.of(parts.get(1)))); + } else if (maybeFediHandle.startsWith("@")) { + return Optional.of(Pair.create(parts.get(0), Optional.empty())); + } else { + return Optional.empty(); + } + } catch (IllegalArgumentException ignored) { + return Optional.empty(); + } + } + // https://mastodon.foo.bar/@User // https://mastodon.foo.bar/@User/43456787654678 // https://pleroma.foo.bar/users/User @@ -921,7 +945,7 @@ public class UiUtils { // https://foo.microblog.pub/o/5b64045effd24f48a27d7059f6cb38f5 // // COPIED FROM https://github.com/tuskyapp/Tusky/blob/develop/app/src/main/java/com/keylesspalace/tusky/util/LinkHelper.kt - public static boolean looksLikeMastodonUrl(String urlString) { + public static boolean looksLikeFediverseUrl(String urlString) { URI uri; try { uri = new URI(urlString); @@ -1088,6 +1112,53 @@ public class UiUtils { }); } + public static boolean acctMatches(String accountID, String acct, String queriedUsername, @Nullable String queriedDomain) { + // check if the username matches + if (!acct.split("@")[0].equalsIgnoreCase(queriedUsername)) return false; + + boolean resultOnHomeInstance = !acct.contains("@"); + if (resultOnHomeInstance) { + // acct is formatted like 'someone' + // only allow home instance result if query didn't specify a domain, + // or the specified domain does, in fact, match the account session's domain + AccountSession session = AccountSessionManager.getInstance().getAccount(accountID); + return queriedDomain == null || session.domain.equalsIgnoreCase(queriedDomain); + } else if (queriedDomain == null) { + // accept whatever result we have as there's no queried domain to compare to + return true; + } else { + // acct is formatted like 'someone@somewhere' + return acct.split("@")[1].equalsIgnoreCase(queriedDomain); + } + } + + public static void lookupAccountHandle(Context context, String accountID, Pair> queryHandle, BiConsumer, Bundle> go) { + String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse("")); + new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true) + .setCallback(new Callback<>() { + @Override + public void onSuccess(SearchResults results) { + Bundle args = new Bundle(); + args.putString("account", accountID); + Optional account = results.accounts.stream() + .filter(a -> acctMatches(accountID, a.acct, queryHandle.first, queryHandle.second.orElse(null))) + .findAny(); + if (account.isPresent()) { + args.putParcelable("profileAccount", Parcels.wrap(account.get())); + go.accept(ProfileFragment.class, args); + return; + } + Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show(); + go.accept(null, null); + } + + @Override + public void onError(ErrorResponse error) { + + } + }).exec(accountID); + } + public static void lookupURL(Context context, String accountID, String url, boolean launchBrowser, BiConsumer, Bundle> go) { Uri uri = Uri.parse(url); List path = uri.getPathSegments(); @@ -1114,7 +1185,7 @@ public class UiUtils { d -> transformDialogForLookup(context, accountID, url, d)) .exec(accountID); return; - } else if (looksLikeMastodonUrl(url)) { + } else if (looksLikeFediverseUrl(url)) { new GetSearchResults(url, null, true) .setCallback(new Callback<>() { @Override diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index d8900891b..4ccd38655 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -290,6 +290,7 @@ This lets you have a content type be pre-selected when creating new posts, overriding the value set in “Posting preferences”. Instance info temporarily unavailable Open in app + Could not open in app Share with account Share or open with account \ No newline at end of file From 2919e109cafc39ab5e9d8af3865d04d83e3e5fd0 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 20:40:18 +0200 Subject: [PATCH 04/79] remove unused member --- .../android/fragments/ThreadFragment.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index d25ad6765..6da5bd388 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -42,24 +42,6 @@ import me.grishka.appkit.api.SimpleCallback; public class ThreadFragment extends StatusListFragment implements ProvidesAssistContent { protected Status mainStatus; - - /** - * lists the hierarchy of ancestors and descendants in a thread. level 0 = the main status. - * e.g. - *
-	 * [0] ancestor:   -2 ↰
-	 * [1] ancestor:     -1 ↰
-	 * [2] main status:     0 ↰
-	 * [3] descendant:        1 ↰
-	 * [4] descendant:          2 ↰
-	 * [5] descendant:            3
-	 * [6] descendant:        1
-	 * [7] descendant:        1 ↰
-	 * [8] descendant:          2
-	 * 
- * confused? good. /j - */ - private final List> levels = new ArrayList<>(); private final HashMap ancestryMap = new HashMap<>(); @Override From f696fcd4124c53a97b36feae16778bab570d7196 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 21:03:47 +0200 Subject: [PATCH 05/79] simplify ancestry code --- .../android/fragments/ThreadFragmentTest.java | 4 +- .../android/fragments/ThreadFragment.java | 66 +++++++------------ 2 files changed, 23 insertions(+), 47 deletions(-) diff --git a/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java b/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java index 524aaed83..22257c58b 100644 --- a/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java +++ b/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java @@ -20,9 +20,7 @@ public class ThreadFragmentTest { } private ThreadFragment.NeighborAncestryInfo fakeInfo(Status s, Status d, Status a) { - ThreadFragment.NeighborAncestryInfo info = new ThreadFragment.NeighborAncestryInfo(s); - info.descendantNeighbor = d; - info.ancestoringNeighbor = a; + ThreadFragment.NeighborAncestryInfo info = new ThreadFragment.NeighborAncestryInfo(s, d, a); return info; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 6da5bd388..4ed5c7c4c 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -2,7 +2,6 @@ package org.joinmastodon.android.fragments; import android.net.Uri; import android.os.Bundle; -import android.util.Pair; import android.view.View; import androidx.annotation.NonNull; @@ -32,11 +31,9 @@ import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import java.util.stream.Stream; import me.grishka.appkit.api.SimpleCallback; @@ -74,10 +71,10 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist NeighborAncestryInfo ancestryInfo = ancestryMap.get(s.id); if (ancestryInfo != null) { item.setAncestryInfo( - ancestryInfo.hasDescendantNeighbor(), - ancestryInfo.hasAncestoringNeighbor(), + ancestryInfo.descendantNeighbor != null, + ancestryInfo.ancestoringNeighbor != null, s.id.equals(mainStatus.id), - ancestryInfo.getAncestoringNeighbor() + Optional.ofNullable(ancestryInfo.ancestoringNeighbor) .map(ancestor -> ancestor.id.equals(mainStatus.id)) .orElse(false) ); @@ -162,22 +159,21 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist int count = statuses.size(); for (int index = 0; index < count; index++) { Status current = statuses.get(index); - NeighborAncestryInfo item = new NeighborAncestryInfo(current); - - item.descendantNeighbor = Optional - .ofNullable(count > index + 1 ? statuses.get(index + 1) : null) - .filter(s -> s.inReplyToId.equals(current.id)) - .orElse(null); - - item.ancestoringNeighbor = Optional.ofNullable(index > 0 ? ancestry.get(index - 1) : null) - .filter(ancestor -> ancestor - .getDescendantNeighbor() - .map(ancestorsDescendant -> ancestorsDescendant.id.equals(current.id)) - .orElse(false)) - .flatMap(NeighborAncestryInfo::getStatus) - .orElse(null); - - ancestry.add(item); + ancestry.add(new NeighborAncestryInfo( + current, + // descendant neighbor + Optional + .ofNullable(count > index + 1 ? statuses.get(index + 1) : null) + .filter(s -> s.inReplyToId.equals(current.id)) + .orElse(null), + // ancestoring neighbor + Optional.ofNullable(index > 0 ? ancestry.get(index - 1) : null) + .filter(ancestor -> Optional.ofNullable(ancestor.descendantNeighbor) + .map(ancestorsDescendant -> ancestorsDescendant.id.equals(current.id)) + .orElse(false)) + .map(a -> a.status) + .orElse(null) + )); } return ancestry; @@ -281,31 +277,13 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist return Uri.parse(mainStatus.url); } - public static class NeighborAncestryInfo { + protected static class NeighborAncestryInfo { protected Status status, descendantNeighbor, ancestoringNeighbor; - public NeighborAncestryInfo(@NonNull Status status) { + protected NeighborAncestryInfo(@NonNull Status status, Status descendantNeighbor, Status ancestoringNeighbor) { this.status = status; - } - - public Optional getStatus() { - return Optional.ofNullable(status); - } - - public Optional getDescendantNeighbor() { - return Optional.ofNullable(descendantNeighbor); - } - - public Optional getAncestoringNeighbor() { - return Optional.ofNullable(ancestoringNeighbor); - } - - public boolean hasDescendantNeighbor() { - return getDescendantNeighbor().isPresent(); - } - - public boolean hasAncestoringNeighbor() { - return getAncestoringNeighbor().isPresent(); + this.descendantNeighbor = descendantNeighbor; + this.ancestoringNeighbor = ancestoringNeighbor; } @Override From 11943571ad5de81e7f302cb7bd9af68e67936761 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 21:08:36 +0200 Subject: [PATCH 06/79] fix thread replies not added to data closes sk22#543 --- .../java/org/joinmastodon/android/fragments/ThreadFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 4ed5c7c4c..ffceafdfe 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -247,6 +247,7 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist protected void onStatusCreated(StatusCreatedEvent ev){ if(ev.status.inReplyToId!=null && getStatusByID(ev.status.inReplyToId)!=null){ + data.add(ev.status); onAppendItems(Collections.singletonList(ev.status)); } } From 6684311ec5da7c1b6e1ff7fe655ff2376690a324 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 21:24:40 +0200 Subject: [PATCH 07/79] fix button state/char counter not updating when empty closes sk22#537 --- .../org/joinmastodon/android/fragments/ComposeFragment.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index e2a87895a..cc6f334b6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -579,8 +579,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr @Override public void afterTextChanged(Editable s){ - if(s.length()==0) + if(s.length()==0){ + updateCharCounter(); return; + } int start=lastChangeStart; int count=lastChangeCount; // offset one char back to catch an already typed '@' or '#' or ':' From 056bfaacfe8e31040b395a6f369244e8dbd46dac Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 21:54:57 +0200 Subject: [PATCH 08/79] fix fab being hidden when scrolling to top closes sk22#528 --- .../android/fragments/BaseStatusListFragment.java | 4 +--- .../android/fragments/ScrollableToTop.java | 5 +++-- .../ui/displayitems/TextStatusDisplayItem.java | 1 - .../org/joinmastodon/android/ui/utils/UiUtils.java | 11 ++++++----- .../android/ui/views/ComposeMediaLayout.java | 1 - .../android/ui/views/MediaGridLayout.java | 1 - mastodon/src/main/res/values/dimens.xml | 1 + 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index ef8a420d1..25f3d2de2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -16,7 +16,6 @@ import android.text.TextPaint; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.animation.TranslateAnimation; import android.widget.ImageButton; @@ -95,7 +94,6 @@ public abstract class BaseStatusListFragment exten @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); - UiUtils.loadMaxWidth(getContext()); if(GlobalUserPreferences.disableMarquee){ setTitleMarqueeEnabled(false); setSubtitleMarqueeEnabled(false); @@ -319,7 +317,7 @@ public abstract class BaseStatusListFragment exten currentPhotoViewer.offsetView(-dx, -dy); View fab = getFab(); - if (fab!=null && GlobalUserPreferences.autoHideFab) { + if (fab!=null && GlobalUserPreferences.autoHideFab && dy != UiUtils.SCROLL_TO_TOP_DELTA) { if (dy > 0 && fab.getVisibility() == View.VISIBLE) { hideFab(); } else if (dy < 0 && fab.getVisibility() != View.VISIBLE) { diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ScrollableToTop.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ScrollableToTop.java index d6a28ae6c..af675748a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ScrollableToTop.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ScrollableToTop.java @@ -3,7 +3,8 @@ package org.joinmastodon.android.fragments; import android.view.ViewTreeObserver; import androidx.recyclerview.widget.RecyclerView; -import me.grishka.appkit.utils.V; + +import org.joinmastodon.android.ui.utils.UiUtils; public interface ScrollableToTop{ void scrollToTop(); @@ -21,7 +22,7 @@ public interface ScrollableToTop{ @Override public boolean onPreDraw(){ list.getViewTreeObserver().removeOnPreDrawListener(this); - list.scrollBy(0, V.dp(300)); + list.scrollBy(0, UiUtils.SCROLL_TO_TOP_DELTA); list.smoothScrollToPosition(0); return true; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java index c6fbd646e..77e1b1c5a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java @@ -65,7 +65,6 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ spoilerEmojiHelper.setText(parsedSpoilerText); } session = AccountSessionManager.getInstance().getAccount(parentFragment.getAccountID()); - UiUtils.loadMaxWidth(parentFragment.getContext()); } public void setTranslationShown(boolean translationShown) { diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 01bc646bd..0adf08595 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -17,6 +17,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.Bitmap; @@ -141,15 +142,11 @@ public class UiUtils { private static Handler mainHandler = new Handler(Looper.getMainLooper()); private static final DateTimeFormatter DATE_FORMATTER_SHORT_WITH_YEAR = DateTimeFormatter.ofPattern("d MMM uuuu"), DATE_FORMATTER_SHORT = DateTimeFormatter.ofPattern("d MMM"); public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT); - public static int MAX_WIDTH; + public static int MAX_WIDTH, SCROLL_TO_TOP_DELTA; private UiUtils() { } - public static void loadMaxWidth(Context ctx) { - if (MAX_WIDTH == 0) MAX_WIDTH = (int) ctx.getResources().getDimension(R.dimen.layout_max_width); - } - public static void launchWebBrowser(Context context, String url) { try { if (GlobalUserPreferences.useCustomTabs) { @@ -898,6 +895,10 @@ public class UiUtils { ColorPalette palette = ColorPalette.palettes.get(GlobalUserPreferences.color); if (palette != null) palette.apply(context); + + Resources res = context.getResources(); + MAX_WIDTH = (int) res.getDimension(R.dimen.layout_max_width); + SCROLL_TO_TOP_DELTA = (int) res.getDimension(R.dimen.scroll_to_top_delta); } public static boolean isDarkTheme() { diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/ComposeMediaLayout.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/ComposeMediaLayout.java index f435cca48..7ee640324 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/views/ComposeMediaLayout.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/ComposeMediaLayout.java @@ -23,7 +23,6 @@ public class ComposeMediaLayout extends ViewGroup{ public ComposeMediaLayout(Context context, AttributeSet attrs, int defStyle){ super(context, attrs, defStyle); - UiUtils.loadMaxWidth(context); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/MediaGridLayout.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/MediaGridLayout.java index d4ca3e547..7d6c49624 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/views/MediaGridLayout.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/MediaGridLayout.java @@ -27,7 +27,6 @@ public class MediaGridLayout extends ViewGroup{ public MediaGridLayout(Context context, AttributeSet attrs, int defStyle){ super(context, attrs, defStyle); - UiUtils.loadMaxWidth(context); } @Override diff --git a/mastodon/src/main/res/values/dimens.xml b/mastodon/src/main/res/values/dimens.xml index 6b401510b..a1ebac297 100644 --- a/mastodon/src/main/res/values/dimens.xml +++ b/mastodon/src/main/res/values/dimens.xml @@ -3,4 +3,5 @@ 220dp 145dp 450dp + 300dp \ No newline at end of file From 7a6af89375e1ea050d04cae8055d7dc4f579847c Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 22:07:58 +0200 Subject: [PATCH 09/79] fix unwanted fab animation when scrolling and switching tab closes sk22#528 --- .../android/fragments/BaseStatusListFragment.java | 11 +++++++++++ .../org/joinmastodon/android/fragments/HasFab.java | 1 + .../joinmastodon/android/fragments/HomeFragment.java | 2 +- .../android/fragments/HomeTabFragment.java | 6 ++++++ .../android/fragments/ProfileFragment.java | 6 ++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 25f3d2de2..1af939d60 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -81,6 +81,7 @@ public abstract class BaseStatusListFragment exten protected HashMap relationships=new HashMap<>(); protected Rect tmpRect=new Rect(); protected TypedObjectPool attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView); + protected boolean currentlyScrolling; public BaseStatusListFragment(){ super(20); @@ -290,6 +291,10 @@ public abstract class BaseStatusListFragment exten fab.startAnimation(animate); } + public boolean isScrolling() { + return currentlyScrolling; + } + @Override public void hideFab() { View fab = getFab(); @@ -330,6 +335,12 @@ public abstract class BaseStatusListFragment exten } } } + + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + currentlyScrolling = newState != RecyclerView.SCROLL_STATE_IDLE; + } }); list.addItemDecoration(new StatusListItemDecoration()); ((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java index 056e80044..937e64757 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java @@ -6,4 +6,5 @@ public interface HasFab { View getFab(); void showFab(); void hideFab(); + boolean isScrolling(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java index 96f4dec28..fbbfb26f3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java @@ -244,7 +244,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene } getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit(); maybeTriggerLoading(newFragment); - if (newFragment instanceof HasFab fabulous) fabulous.showFab(); + if (newFragment instanceof HasFab fabulous && !fabulous.isScrolling()) fabulous.showFab(); currentTab=tab; ((FragmentStackActivity)getActivity()).invalidateSystemBarColors(this); if (tab == R.id.tab_search && isPleroma) searchFragment.selectSearch(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java index eca5a2a01..98d0516cc 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java @@ -460,6 +460,12 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment l) l.hideFab(); } + @Override + public boolean isScrolling() { + return (fragments[pager.getCurrentItem()] instanceof HasFab fabulous) + && fabulous.isScrolling(); + } + private void updateSwitcherIcon(int i) { timelineIcon.setImageResource(timelines[i].getIcon().iconRes); timelineTitle.setText(timelines[i].getTitle(getContext())); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 6839c027a..9c706a3e7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -775,6 +775,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList if (getFragmentForPage(pager.getCurrentItem()) instanceof HasFab fabulous) fabulous.hideFab(); } + @Override + public boolean isScrolling() { + return getFragmentForPage(pager.getCurrentItem()) instanceof HasFab fabulous + && fabulous.isScrolling(); + } + private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){ int topBarsH=getToolbar().getHeight()+statusBarHeight; if(scrollY>avatarBorder.getTop()-topBarsH){ From 37622ba9ce15a748a920c5742b020762a780915f Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 3 Jun 2023 22:47:20 +0200 Subject: [PATCH 10/79] generalize notification handling, open reports in browser --- .../joinmastodon/android/MainActivity.java | 14 +------------ .../fragments/NotificationsListFragment.java | 21 ++++--------------- .../android/ui/utils/UiUtils.java | 17 +++++++++++++++ 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java index fd43c387b..1f19516b8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java @@ -117,25 +117,13 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis } private void showFragmentForNotification(Notification notification, String accountID){ - Fragment fragment; - Bundle args=new Bundle(); - args.putString("account", accountID); - args.putBoolean("_can_go_back", true); try{ notification.postprocess(); }catch(ObjectValidationException x){ Log.w("MainActivity", x); return; } - if(notification.status!=null){ - fragment=new ThreadFragment(); - args.putParcelable("status", Parcels.wrap(notification.status)); - }else{ - fragment=new ProfileFragment(); - args.putParcelable("profileAccount", Parcels.wrap(notification.account)); - } - fragment.setArguments(args); - showFragment(fragment); + UiUtils.showFragmentForNotification(this, notification, accountID, null); } private void showFragmentForExternalShare(Bundle args) { diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java index 176adcfe1..6650e6752 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java @@ -192,23 +192,10 @@ public class NotificationsListFragment extends BaseStatusListFragment Date: Sat, 3 Jun 2023 23:39:43 +0200 Subject: [PATCH 11/79] support parsing mailto links i mean, why not - if github decided every @username@example.social is actually an email address, might as well support sharing that mailto link to megalodon --- .../android/ui/utils/UiUtilsTest.java | 19 ++++++++----- .../android/ExternalShareActivity.java | 2 +- .../android/ui/utils/UiUtils.java | 27 ++++++++++--------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java b/mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java index 2fd84eb77..63c3fb866 100644 --- a/mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java +++ b/mastodon/src/androidTest/java/org/joinmastodon/android/ui/utils/UiUtilsTest.java @@ -29,35 +29,40 @@ public class UiUtilsTest { } @Test - public void looksLikeFediverseHandle() { + public void parseFediverseHandle() { assertEquals( Optional.of(Pair.create("megalodon", Optional.of("floss.social"))), - UiUtils.looksLikeFediverseHandle("megalodon@floss.social") + UiUtils.parseFediverseHandle("megalodon@floss.social") ); assertEquals( Optional.of(Pair.create("megalodon", Optional.of("floss.social"))), - UiUtils.looksLikeFediverseHandle("@megalodon@floss.social") + UiUtils.parseFediverseHandle("@megalodon@floss.social") ); assertEquals( Optional.of(Pair.create("megalodon", Optional.empty())), - UiUtils.looksLikeFediverseHandle("@megalodon") + UiUtils.parseFediverseHandle("@megalodon") + ); + + assertEquals( + Optional.of(Pair.create("megalodon", Optional.of("floss.social"))), + UiUtils.parseFediverseHandle("mailto:megalodon@floss.social") ); assertEquals( Optional.empty(), - UiUtils.looksLikeFediverseHandle("megalodon") + UiUtils.parseFediverseHandle("megalodon") ); assertEquals( Optional.empty(), - UiUtils.looksLikeFediverseHandle("this is not a fedi handle") + UiUtils.parseFediverseHandle("this is not a fedi handle") ); assertEquals( Optional.empty(), - UiUtils.looksLikeFediverseHandle("not@a-domain") + UiUtils.parseFediverseHandle("not@a-domain") ); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java index d1ad5468b..972e7504d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java @@ -33,7 +33,7 @@ public class ExternalShareActivity extends FragmentStackActivity{ if(savedInstanceState==null){ Optional text = Optional.ofNullable(getIntent().getStringExtra(Intent.EXTRA_TEXT)); - Optional>> fediHandle = text.flatMap(UiUtils::looksLikeFediverseHandle); + Optional>> fediHandle = text.flatMap(UiUtils::parseFediverseHandle); boolean isFediUrl = text.map(UiUtils::looksLikeFediverseUrl).orElse(false); boolean isOpenable = isFediUrl || fediHandle.isPresent(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 7cace4851..25ca9e1d0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -907,25 +907,28 @@ public class UiUtils { return theme == GlobalUserPreferences.ThemePreference.DARK; } - public static Optional>> looksLikeFediverseHandle(String maybeFediHandle) { + public static Optional>> parseFediverseHandle(String maybeFediHandle) { // https://stackoverflow.com/a/26987741, except i put a + here ... v String domainRegex = "^(((?!-))(xn--|_)?[a-z0-9-]{0,61}[a-z0-9]\\.)+(xn--)?([a-z0-9][a-z0-9\\-]{0,60}|[a-z0-9-]{1,30}\\.[a-z]{2,})$"; - try { - List parts = Arrays.stream(maybeFediHandle.split("@")) - .filter(part -> !part.isEmpty()) - .collect(Collectors.toList()); - if (parts.size() == 0 || !parts.get(0).matches("^[^/\\s]+$")) { - return Optional.empty(); - } else if (parts.size() == 2) { + if (maybeFediHandle.toLowerCase().startsWith("mailto:")) { + maybeFediHandle = maybeFediHandle.substring("mailto:".length()); + } + List parts = Arrays.stream(maybeFediHandle.split("@")) + .filter(part -> !part.isEmpty()) + .collect(Collectors.toList()); + if (parts.size() == 0 || !parts.get(0).matches("^[^/\\s]+$")) { + return Optional.empty(); + } else if (parts.size() == 2) { + try { String domain = IDN.toASCII(parts.get(1)); if (!domain.matches(domainRegex)) return Optional.empty(); return Optional.of(Pair.create(parts.get(0), Optional.of(parts.get(1)))); - } else if (maybeFediHandle.startsWith("@")) { - return Optional.of(Pair.create(parts.get(0), Optional.empty())); - } else { + } catch (IllegalArgumentException ignored) { return Optional.empty(); } - } catch (IllegalArgumentException ignored) { + } else if (maybeFediHandle.startsWith("@")) { + return Optional.of(Pair.create(parts.get(0), Optional.empty())); + } else { return Optional.empty(); } } From 336a8194bdda0073c0e9c3a0a2ddaa917752da4a Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 01:36:05 +0200 Subject: [PATCH 12/79] fix settings button binding not reset visibility and events --- .../android/fragments/SettingsFragment.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index fcd26b27d..a7b4f727a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -1084,14 +1084,17 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide button=findViewById(R.id.button); } + @SuppressLint("ClickableViewAccessibility") @Override public void onBind(ButtonItem item){ text.setText(item.text); - if (item.icon == 0) { - icon.setVisibility(View.GONE); - } else { - icon.setImageResource(item.icon); - } + icon.setVisibility(item.icon == 0 ? View.GONE : View.VISIBLE); + icon.setImageResource(item.icon == 0 ? 0 : item.icon); + // reset listeners before letting the button consumer consume the button + // (and potentially set some listeners, but not others) + button.setOnTouchListener(null); + button.setOnClickListener(null); + button.setOnLongClickListener(null); item.buttonConsumer.accept(button); } } From 44a4d02815ef1f3a0c2d13557bee5d237f68f534 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 01:36:38 +0200 Subject: [PATCH 13/79] remove redundant suppress annotation --- .../org/joinmastodon/android/fragments/SettingsFragment.java | 1 - 1 file changed, 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index a7b4f727a..a36b18a5d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -1076,7 +1076,6 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide private final ImageView icon; private final TextView text; - @SuppressLint("ClickableViewAccessibility") public ButtonViewHolder(){ super(getActivity(), R.layout.item_settings_button, list); text=findViewById(R.id.text); From 486eef21dd5dadb2ba2a57aac5a3f096ff21482c Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 02:16:47 +0200 Subject: [PATCH 14/79] responsive footer width --- .../android/ui/views/MaxWidthFrameLayout.java | 15 +- .../main/res/layout/display_item_footer.xml | 203 +++++++++--------- mastodon/src/main/res/values/attrs.xml | 1 + 3 files changed, 119 insertions(+), 100 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/views/MaxWidthFrameLayout.java b/mastodon/src/main/java/org/joinmastodon/android/ui/views/MaxWidthFrameLayout.java index 364f030f1..5625037c1 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/views/MaxWidthFrameLayout.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/views/MaxWidthFrameLayout.java @@ -3,12 +3,13 @@ package org.joinmastodon.android.ui.views; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; +import android.view.ViewGroup; import android.widget.FrameLayout; import org.joinmastodon.android.R; public class MaxWidthFrameLayout extends FrameLayout{ - private int maxWidth; + private int maxWidth, defaultWidth; public MaxWidthFrameLayout(Context context){ this(context, null); @@ -22,6 +23,7 @@ public class MaxWidthFrameLayout extends FrameLayout{ super(context, attrs, defStyle); TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.MaxWidthFrameLayout); maxWidth=ta.getDimensionPixelSize(R.styleable.MaxWidthFrameLayout_android_maxWidth, Integer.MAX_VALUE); + defaultWidth=ta.getDimensionPixelSize(R.styleable.MaxWidthFrameLayout_defaultWidth, -1); ta.recycle(); } @@ -33,10 +35,19 @@ public class MaxWidthFrameLayout extends FrameLayout{ this.maxWidth=maxWidth; } + public int getDefaultWidth() { + return defaultWidth; + } + + public void setDefaultWidth(int defaultWidth) { + this.defaultWidth = defaultWidth; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ if(MeasureSpec.getSize(widthMeasureSpec)>maxWidth){ - widthMeasureSpec=maxWidth | MeasureSpec.getMode(widthMeasureSpec); + int width = defaultWidth >= 0 ? defaultWidth : maxWidth; + widthMeasureSpec=width | MeasureSpec.getMode(widthMeasureSpec); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } diff --git a/mastodon/src/main/res/layout/display_item_footer.xml b/mastodon/src/main/res/layout/display_item_footer.xml index 99896e58f..23e7edef0 100644 --- a/mastodon/src/main/res/layout/display_item_footer.xml +++ b/mastodon/src/main/res/layout/display_item_footer.xml @@ -1,120 +1,127 @@ - + android:layout_height="wrap_content"> + - - - + android:minWidth="56dp"> + + - + - - - + android:minWidth="56dp"> + + - + - - - + android:minWidth="56dp"> + + - + - - - + android:minWidth="56dp"> + + - + - - - + android:layout_height="match_parent"> + + - \ No newline at end of file + + \ No newline at end of file diff --git a/mastodon/src/main/res/values/attrs.xml b/mastodon/src/main/res/values/attrs.xml index 5755462c2..86ffa2122 100644 --- a/mastodon/src/main/res/values/attrs.xml +++ b/mastodon/src/main/res/values/attrs.xml @@ -73,6 +73,7 @@ + From bc717f5b1054ba77cf9040aaa2f8193c11861f19 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 04:08:38 +0200 Subject: [PATCH 15/79] tweak footer margins and hitboxes --- .../displayitems/FooterStatusDisplayItem.java | 10 +- .../main/res/layout/display_item_footer.xml | 168 +++++++++--------- 2 files changed, 92 insertions(+), 86 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java index 75b24fc45..d4e50917b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java @@ -56,8 +56,8 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } public static class Holder extends StatusDisplayItem.Holder{ - private final TextView reply, boost, favorite, bookmark; - private final ImageView share; + private final TextView reply, boost, favorite; + private final ImageView share, bookmark; private static final Animation opacityOut, opacityIn; private View touchingView = null; @@ -100,7 +100,6 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ UiUtils.fixCompoundDrawableTintOnAndroid6(reply); UiUtils.fixCompoundDrawableTintOnAndroid6(boost); UiUtils.fixCompoundDrawableTintOnAndroid6(favorite); - UiUtils.fixCompoundDrawableTintOnAndroid6(bookmark); } View reply=findViewById(R.id.reply_btn); View boost=findViewById(R.id.boost_btn); @@ -181,8 +180,9 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } else if (action == MotionEvent.ACTION_DOWN) { longClickPerformed = false; touchingView = v; - // 20dp to center in middle of icon, because: (icon width = 24dp) / 2 + (paddingStart = 8dp) - v.setPivotX(V.dp(20)); + // 28dp to center in middle of icon, because: + // (icon width = 24dp) / 2 + (paddingStart = 8dp) + (paddingHorizontal = 8dp) + v.setPivotX(V.dp(28)); v.animate().scaleX(0.85f).scaleY(0.85f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(75).start(); if (disabled) return true; v.postDelayed(longClickRunnable, ViewConfiguration.getLongPressTimeout()); diff --git a/mastodon/src/main/res/layout/display_item_footer.xml b/mastodon/src/main/res/layout/display_item_footer.xml index 23e7edef0..dcd358586 100644 --- a/mastodon/src/main/res/layout/display_item_footer.xml +++ b/mastodon/src/main/res/layout/display_item_footer.xml @@ -3,122 +3,128 @@ xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:maxWidth="600dp" - app:defaultWidth="350dp" + app:defaultWidth="450dp" android:layout_width="match_parent" android:layout_height="wrap_content"> + + android:layout_height="48dp"> - + + android:paddingHorizontal="16dp" + android:minWidth="56dp"> + + - - - + + android:paddingHorizontal="16dp" + android:minWidth="56dp"> + + - - - + + android:paddingHorizontal="16dp" + android:minWidth="56dp"> + + - - - + + android:paddingHorizontal="16dp" + android:minWidth="56dp"> + + - - + android:layout_height="match_parent" + android:paddingHorizontal="16dp"> From 60ccf5cf0a765751265fa58d5a84fe62d477d5c2 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 04:15:15 +0200 Subject: [PATCH 16/79] only shift selection box if footer is present --- .../android/fragments/BaseStatusListFragment.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 1af939d60..96480351e 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -34,6 +34,7 @@ import org.joinmastodon.android.model.Relationship; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.BetterItemAnimator; import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem; @@ -384,7 +385,9 @@ public abstract class BaseStatusListFragment exten } // shifting the selection box down // see also: FooterStatusDisplayItem#onBind (setMargins) - if (isWarning || firstIndex < 0 || lastIndex < 0) return; + if (isWarning || firstIndex < 0 || lastIndex < 0 || + !(list.getChildViewHolder(list.getChildAt(lastIndex)) + instanceof FooterStatusDisplayItem.Holder)) return; int prevIndex = firstIndex - 1, nextIndex = lastIndex + 1; boolean prevIsWarning = prevIndex > 0 && prevIndex < list.getChildCount() && list.getChildViewHolder(list.getChildAt(prevIndex)) From 5432f2590c0968cf8d90e82f9aac822a704244d3 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 05:00:48 +0200 Subject: [PATCH 17/79] fine-tune footer layout --- .../displayitems/TextStatusDisplayItem.java | 12 +++++----- mastodon/src/main/res/drawable/ic_boost.xml | 6 ++--- ...c_fluent_arrow_repeat_all_24sp_regular.xml | 3 +++ ...uent_arrow_repeat_all_24sp_very_filled.xml | 23 +++++++++++++++++++ ...uent_arrow_repeat_all_off_24sp_regular.xml | 3 +++ .../ic_fluent_bookmark_24sp_filled.xml | 3 +++ .../ic_fluent_bookmark_24sp_regular.xml | 3 +++ .../ic_fluent_bookmark_24sp_selector.xml | 8 +++++++ .../ic_fluent_chat_multiple_24sp_regular.xml | 3 +++ ...fluent_chat_multiple_24sp_regular_text.xml | 15 ++++++++++++ ...luent_chat_multiple_24sp_selector_text.xml | 6 +++++ .../drawable/ic_fluent_share_24sp_regular.xml | 3 +++ .../drawable/ic_fluent_star_24sp_filled.xml | 3 +++ .../drawable/ic_fluent_star_24sp_regular.xml | 3 +++ .../drawable/ic_fluent_star_24sp_selector.xml | 8 +++++++ .../main/res/layout/display_item_footer.xml | 23 +++++++++++-------- 16 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java index 77e1b1c5a..a6d419c49 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java @@ -238,12 +238,12 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ // remove additional padding when (transparently padded) translate button is visible int pos = getAbsoluteAdapterPosition(); - itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), - (translateVisible && - item.parentFragment.getDisplayItems().size() >= pos + 1 && - item.parentFragment.getDisplayItems().get(pos + 1) instanceof FooterStatusDisplayItem) - ? 0 : V.dp(12) - ); + boolean nextIsFooter = item.parentFragment.getDisplayItems().size() >= pos + 1 && + item.parentFragment.getDisplayItems().get(pos + 1) instanceof FooterStatusDisplayItem; + int bottomPadding = (translateVisible && nextIsFooter) ? 0 + : nextIsFooter ? V.dp(8) + : V.dp(12); + itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), bottomPadding); if (!GlobalUserPreferences.collapseLongPosts) { textScrollView.setLayoutParams(wrapParams); diff --git a/mastodon/src/main/res/drawable/ic_boost.xml b/mastodon/src/main/res/drawable/ic_boost.xml index 64f632e5b..a14cebaa2 100644 --- a/mastodon/src/main/res/drawable/ic_boost.xml +++ b/mastodon/src/main/res/drawable/ic_boost.xml @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml new file mode 100644 index 000000000..ee22e43a2 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml new file mode 100644 index 000000000..15d5e44f6 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml new file mode 100644 index 000000000..bf315efcb --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml new file mode 100644 index 000000000..2412d01ac --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml new file mode 100644 index 000000000..c32c5603b --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml new file mode 100644 index 000000000..9e81bec2b --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml new file mode 100644 index 000000000..98a4373b4 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml new file mode 100644 index 000000000..3adbf88f5 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml new file mode 100644 index 000000000..4e3f84614 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml new file mode 100644 index 000000000..b2e6ddd0b --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml new file mode 100644 index 000000000..92118cbfb --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml new file mode 100644 index 000000000..8c946162a --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml new file mode 100644 index 000000000..faf7363fa --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/mastodon/src/main/res/layout/display_item_footer.xml b/mastodon/src/main/res/layout/display_item_footer.xml index dcd358586..3f92635db 100644 --- a/mastodon/src/main/res/layout/display_item_footer.xml +++ b/mastodon/src/main/res/layout/display_item_footer.xml @@ -10,7 +10,9 @@ + android:layout_height="wrap_content" + android:paddingHorizontal="11sp"> + + android:paddingVertical="12dp"> + android:paddingVertical="12dp"> + android:paddingVertical="12dp"> + android:paddingVertical="12dp"> @@ -118,13 +120,14 @@ android:id="@+id/share_btn" android:layout_width="wrap_content" android:layout_height="match_parent" - android:paddingHorizontal="16dp"> + android:paddingHorizontal="16dp" + android:paddingVertical="12dp"> From 4ca1a7b29ee8d569c04e25d174099dc6f0614844 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 11:45:12 +0200 Subject: [PATCH 18/79] fix index out of bounds exception --- .../android/ui/displayitems/TextStatusDisplayItem.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java index a6d419c49..5cb152f0f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java @@ -237,9 +237,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ spaceBelowText.setVisibility(translateVisible ? View.VISIBLE : View.GONE); // remove additional padding when (transparently padded) translate button is visible - int pos = getAbsoluteAdapterPosition(); - boolean nextIsFooter = item.parentFragment.getDisplayItems().size() >= pos + 1 && - item.parentFragment.getDisplayItems().get(pos + 1) instanceof FooterStatusDisplayItem; + int nextPos = getAbsoluteAdapterPosition(); + boolean nextIsFooter = item.parentFragment.getDisplayItems().size() > nextPos && + item.parentFragment.getDisplayItems().get(nextPos) instanceof FooterStatusDisplayItem; int bottomPadding = (translateVisible && nextIsFooter) ? 0 : nextIsFooter ? V.dp(8) : V.dp(12); From 890340de9468a3f47cf1f0461c579d7315a87655 Mon Sep 17 00:00:00 2001 From: Andrewblasco Date: Sat, 3 Jun 2023 09:44:47 +0000 Subject: [PATCH 19/79] Translated using Weblate (Spanish) Currently translated at 99.6% (291 of 292 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/ --- mastodon/src/main/res/values-es-rES/strings_sk.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mastodon/src/main/res/values-es-rES/strings_sk.xml b/mastodon/src/main/res/values-es-rES/strings_sk.xml index c8dc06230..c0a685e9a 100644 --- a/mastodon/src/main/res/values-es-rES/strings_sk.xml +++ b/mastodon/src/main/res/values-es-rES/strings_sk.xml @@ -286,4 +286,10 @@ Contenido por defecto Permite establecer un tipo de contenido como Markdown al crear una entrada. Ten en cuenta que no todas las instancias lo admiten. Permite preseleccionar un tipo de contenido al crear nuevas entradas, anulando el valor establecido en \"Preferencias de publicación\". + Estas son las publicaciones más recientes de la gente en tu servidor de Akkoma. + Burbuja + Información de la instancia temporalmente no disponible + Compartir o abrir con una cuenta + Abrir en la app + Compartir con una cuenta \ No newline at end of file From 968b2ee4600eb7863db7feaf17a326f2f5204669 Mon Sep 17 00:00:00 2001 From: Eryk Michalak Date: Sat, 3 Jun 2023 01:29:30 +0000 Subject: [PATCH 20/79] Translated using Weblate (Polish) Currently translated at 97.9% (286 of 292 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/ --- mastodon/src/main/res/values-pl-rPL/strings_sk.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mastodon/src/main/res/values-pl-rPL/strings_sk.xml b/mastodon/src/main/res/values-pl-rPL/strings_sk.xml index d4822e6b0..42e9fbeab 100644 --- a/mastodon/src/main/res/values-pl-rPL/strings_sk.xml +++ b/mastodon/src/main/res/values-pl-rPL/strings_sk.xml @@ -274,4 +274,15 @@ Potwierdź przed podbiciem zareagował(a) z %s zareagował(a) + Domyślny rodzaj treści + Informacje o instancji są tymczasowo niedostępne + HTML + BBCode + MFM + Rodzaj treści + Nie określono + Czysty tekst + Włącz formatowanie wpisu + Otwórz w aplikacji + Udostępnij z kontem \ No newline at end of file From b102deaee11572bebb7a5780359e6626434b6386 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 19:08:18 +0200 Subject: [PATCH 21/79] don't let interaction counts go negative --- .../joinmastodon/android/api/StatusInteractionController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java b/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java index ec45b6d90..8baabf20a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/StatusInteractionController.java @@ -46,7 +46,7 @@ public class StatusInteractionController{ @Override public void onSuccess(Status result){ runningFavoriteRequests.remove(status.id); - result.favouritesCount = Math.max(0, status.favouritesCount) + (favorited ? 1 : -1); + result.favouritesCount = Math.max(0, status.favouritesCount + (favorited ? 1 : -1)); cb.accept(result); if (updateCounters) E.post(new StatusCountersUpdatedEvent(result)); } @@ -80,7 +80,7 @@ public class StatusInteractionController{ public void onSuccess(Status reblog){ Status result = reblog.getContentStatus(); runningReblogRequests.remove(status.id); - result.reblogsCount = Math.max(0, status.reblogsCount) + (reblogged ? 1 : -1); + result.reblogsCount = Math.max(0, status.reblogsCount + (reblogged ? 1 : -1)); cb.accept(result); if (updateCounters) E.post(new StatusCountersUpdatedEvent(result)); } From 0d5fa97800c334a3ba4395e22927030833a3a079 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 20:40:27 +0200 Subject: [PATCH 22/79] fix wrong index --- .../android/ui/displayitems/TextStatusDisplayItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java index 5cb152f0f..8154af905 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java @@ -237,7 +237,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ spaceBelowText.setVisibility(translateVisible ? View.VISIBLE : View.GONE); // remove additional padding when (transparently padded) translate button is visible - int nextPos = getAbsoluteAdapterPosition(); + int nextPos = getAbsoluteAdapterPosition() + 1; boolean nextIsFooter = item.parentFragment.getDisplayItems().size() > nextPos && item.parentFragment.getDisplayItems().get(nextPos) instanceof FooterStatusDisplayItem; int bottomPadding = (translateVisible && nextIsFooter) ? 0 From 2f02a238dfa20b728387f718edeea668e20be914 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 20:56:44 +0200 Subject: [PATCH 23/79] refresh updated main status --- .../android/fragments/ThreadFragmentTest.java | 30 ++++++++-- .../android/fragments/ThreadFragment.java | 59 ++++++++++++++++++- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java b/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java index 22257c58b..4e17aceb4 100644 --- a/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java +++ b/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java @@ -2,14 +2,14 @@ package org.joinmastodon.android.fragments; import static org.junit.Assert.*; -import android.util.Pair; - +import org.joinmastodon.android.events.StatusCountersUpdatedEvent; +import org.joinmastodon.android.events.StatusUpdatedEvent; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.StatusContext; import org.junit.Test; +import java.time.Instant; import java.util.List; -import java.util.stream.Collectors; public class ThreadFragmentTest { @@ -20,8 +20,7 @@ public class ThreadFragmentTest { } private ThreadFragment.NeighborAncestryInfo fakeInfo(Status s, Status d, Status a) { - ThreadFragment.NeighborAncestryInfo info = new ThreadFragment.NeighborAncestryInfo(s, d, a); - return info; + return new ThreadFragment.NeighborAncestryInfo(s, d, a); } @Test @@ -53,6 +52,27 @@ public class ThreadFragmentTest { ), neighbors); } + @Test + public void updateMainStatus() { + ThreadFragment fragment = new ThreadFragment(); + fragment.mainStatus = Status.ofFake("123456", "original text", Instant.EPOCH); + + Status update1 = Status.ofFake("123456", "updated text", Instant.EPOCH); + update1.editedAt = Instant.ofEpochSecond(1); + fragment.updatedStatus = update1; + StatusUpdatedEvent event1 = (StatusUpdatedEvent) fragment.updateMainStatus(); + assertEquals("fired update event", update1, event1.status); + assertEquals("updated main status", update1, fragment.mainStatus); + + Status update2 = Status.ofFake("123456", "updated text", Instant.EPOCH); + update2.favouritesCount = 123; + fragment.updatedStatus = update2; + StatusCountersUpdatedEvent event2 = (StatusCountersUpdatedEvent) fragment.updateMainStatus(); + assertEquals("only fired counter update event", update2.id, event2.id); + assertEquals("updated counter is correct", 123, event2.favorites); + assertEquals("updated main status", update2, fragment.mainStatus); + } + @Test public void sortStatusContext() { StatusContext context = new StatusContext(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index ffceafdfe..4020cd692 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -5,14 +5,20 @@ import android.os.Bundle; import android.view.View; import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import org.joinmastodon.android.E; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.requests.statuses.GetStatusByID; import org.joinmastodon.android.api.requests.statuses.GetStatusContext; +import org.joinmastodon.android.events.StatusCountersUpdatedEvent; import org.joinmastodon.android.events.StatusCreatedEvent; +import org.joinmastodon.android.events.StatusUpdatedEvent; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.StatusContext; +import org.joinmastodon.android.ui.BetterItemAnimator; import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem; @@ -35,11 +41,14 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; +import me.grishka.appkit.api.Callback; +import me.grishka.appkit.api.ErrorResponse; import me.grishka.appkit.api.SimpleCallback; public class ThreadFragment extends StatusListFragment implements ProvidesAssistContent { - protected Status mainStatus; + protected Status mainStatus, updatedStatus; private final HashMap ancestryMap = new HashMap<>(); + private boolean initialAnimationFinished; @Override public void onCreate(Bundle savedInstanceState){ @@ -101,11 +110,12 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist @Override protected void doLoadData(int offset, int count){ + refreshMainStatus(); currentRequest=new GetStatusContext(mainStatus.id) .setCallback(new SimpleCallback<>(this){ @Override public void onSuccess(StatusContext result){ - if (getActivity() == null) return; + if (getContext() == null) return; if(refreshing){ data.clear(); ancestryMap.clear(); @@ -149,6 +159,40 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist .exec(accountID); } + private void refreshMainStatus() { + new GetStatusByID(mainStatus.id) + .setCallback(new Callback<>() { + @Override + public void onSuccess(Status status) { + if (getContext() == null || status == null) return; + updatedStatus = status; + // only update main status if the initial animation is already finished. + // otherwise, the animator will call it in onAnimationFinished + if (initialAnimationFinished || data.size() == 1) updateMainStatus(); + } + + @Override + public void onError(ErrorResponse error) {} + }).exec(accountID); + } + + protected Object updateMainStatus() { + // returning fired event object to facilitate testing + Object event; + if (updatedStatus.editedAt != null && + (mainStatus.editedAt == null || + updatedStatus.editedAt.isAfter(mainStatus.editedAt))) { + event = new StatusUpdatedEvent(updatedStatus); + } else { + event = new StatusCountersUpdatedEvent(updatedStatus); + } + + mainStatus = updatedStatus; + updatedStatus = null; + E.post(event); + return event; + } + public static List mapNeighborhoodAncestry(Status mainStatus, StatusContext context) { List ancestry = new ArrayList<>(); @@ -243,6 +287,17 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist showContent(); if(!loaded) footerProgress.setVisibility(View.VISIBLE); + list.setItemAnimator(new BetterItemAnimator() { + @Override + public void onAnimationFinished(@NonNull RecyclerView.ViewHolder viewHolder) { + super.onAnimationFinished(viewHolder); + // in case someone else is about to call updateMainStatus faster... + initialAnimationFinished = true; + // ...if not (someone did fetch it but the animation wasn't finished yet), + // call it now + if (updatedStatus != null) updateMainStatus(); + } + }); } protected void onStatusCreated(StatusCreatedEvent ev){ From f090ca7f750d4482994a97a492e48a1d520365d7 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 21:08:45 +0200 Subject: [PATCH 24/79] use sp for scaled footer --- .../ui/displayitems/FooterStatusDisplayItem.java | 2 +- .../org/joinmastodon/android/ui/utils/UiUtils.java | 10 ++++++++++ mastodon/src/main/res/layout/display_item_footer.xml | 4 ++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java index d4e50917b..ea30eb1e4 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java @@ -182,7 +182,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ touchingView = v; // 28dp to center in middle of icon, because: // (icon width = 24dp) / 2 + (paddingStart = 8dp) + (paddingHorizontal = 8dp) - v.setPivotX(V.dp(28)); + v.setPivotX(UiUtils.sp(v.getContext(), 28)); v.animate().scaleX(0.85f).scaleY(0.85f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(75).start(); if (disabled) return true; v.postDelayed(longClickRunnable, ViewConfiguration.getLongPressTimeout()); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 25ca9e1d0..0988e589b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -1407,6 +1407,16 @@ public class UiUtils { } } + /** + * Scale the input value according to the device's scaled display density + * @param sp Input value in scale-independent pixels (sp) + * @return Scaled value in physical pixels (px) + */ + public static int sp(Context context, float sp){ + // TODO: replace with V.sp in next AppKit version + return Math.round(sp*context.getApplicationContext().getResources().getDisplayMetrics().scaledDensity); + } + /** * Wraps a View.OnClickListener to filter multiple clicks in succession. * Useful for buttons that perform some action that changes their state asynchronously. diff --git a/mastodon/src/main/res/layout/display_item_footer.xml b/mastodon/src/main/res/layout/display_item_footer.xml index 3f92635db..e1a173b2b 100644 --- a/mastodon/src/main/res/layout/display_item_footer.xml +++ b/mastodon/src/main/res/layout/display_item_footer.xml @@ -2,8 +2,8 @@ From 6522403c371119b18322c0441489294f9feaafaa Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 22:12:45 +0200 Subject: [PATCH 25/79] fix footer text margins --- .../displayitems/FooterStatusDisplayItem.java | 43 +++++------ mastodon/src/main/res/color/boost_icon.xml | 2 +- .../main/res/layout/display_item_footer.xml | 71 +++++++++++++------ mastodon/src/main/res/values/attrs.xml | 1 + mastodon/src/main/res/values/styles.xml | 2 + 5 files changed, 71 insertions(+), 48 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java index ea30eb1e4..a0dbdcc05 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java @@ -56,8 +56,8 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } public static class Holder extends StatusDisplayItem.Holder{ - private final TextView reply, boost, favorite; - private final ImageView share, bookmark; + private final TextView replies, boosts, favorites; + private final View reply, boost, favorite, share, bookmark; private static final Animation opacityOut, opacityIn; private View touchingView = null; @@ -91,21 +91,16 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ public Holder(Activity activity, ViewGroup parent){ super(activity, R.layout.display_item_footer, parent); - reply=findViewById(R.id.reply); - boost=findViewById(R.id.boost); - favorite=findViewById(R.id.favorite); - bookmark=findViewById(R.id.bookmark); - share=findViewById(R.id.share); - if(Build.VERSION.SDK_INTboostConsumer(v, r)); } private void boostConsumer(View v, Status r) { v.startAnimation(opacityIn); - bindButton(boost, r.reblogsCount); + bindButton(boosts, r.reblogsCount); } private boolean onBoostLongClick(View v){ @@ -312,10 +307,10 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void onFavoriteClick(View v){ - favorite.setSelected(!item.status.favourited); + favorites.setSelected(!item.status.favourited); AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{ v.startAnimation(opacityIn); - bindButton(favorite, r.favouritesCount); + bindButton(favorites, r.favouritesCount); }); } diff --git a/mastodon/src/main/res/color/boost_icon.xml b/mastodon/src/main/res/color/boost_icon.xml index e50a9554a..9efc146c8 100644 --- a/mastodon/src/main/res/color/boost_icon.xml +++ b/mastodon/src/main/res/color/boost_icon.xml @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/display_item_footer.xml b/mastodon/src/main/res/layout/display_item_footer.xml index e1a173b2b..420f8b529 100644 --- a/mastodon/src/main/res/layout/display_item_footer.xml +++ b/mastodon/src/main/res/layout/display_item_footer.xml @@ -18,80 +18,105 @@ android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent"> - + - + tools:text="123" + tools:ignore="RtlSymmetry" /> + - + - + tools:text="123" + tools:ignore="RtlSymmetry" /> + - + - + tools:text="123" + tools:ignore="RtlSymmetry" /> + diff --git a/mastodon/src/main/res/values/attrs.xml b/mastodon/src/main/res/values/attrs.xml index 86ffa2122..95b630003 100644 --- a/mastodon/src/main/res/values/attrs.xml +++ b/mastodon/src/main/res/values/attrs.xml @@ -21,6 +21,7 @@ + diff --git a/mastodon/src/main/res/values/styles.xml b/mastodon/src/main/res/values/styles.xml index c29fc5452..12e31868e 100644 --- a/mastodon/src/main/res/values/styles.xml +++ b/mastodon/src/main/res/values/styles.xml @@ -28,6 +28,7 @@ ?colorGray100 ?colorGray800 ?colorGray500 + ?colorGray300 ?colorGray50 #E9EDF2 ?colorGray50 @@ -127,6 +128,7 @@ ?colorGray700 ?colorGray50 ?colorGray400 + ?colorGray500 ?colorGray800 #E9EDF2 ?colorGray700 From 2aeb5f03d6aba378b1f24682ff5c629bbfb1e96b Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 22:32:54 +0200 Subject: [PATCH 26/79] remove unused sp drawables --- mastodon/src/main/res/drawable/ic_boost.xml | 6 ++-- ...c_fluent_arrow_repeat_all_24sp_regular.xml | 3 -- ...uent_arrow_repeat_all_24sp_very_filled.xml | 23 --------------- ...uent_arrow_repeat_all_off_24sp_regular.xml | 3 -- .../ic_fluent_bookmark_24sp_filled.xml | 3 -- .../ic_fluent_bookmark_24sp_regular.xml | 3 -- .../ic_fluent_bookmark_24sp_selector.xml | 8 ------ .../ic_fluent_chat_multiple_24sp_regular.xml | 3 -- ...fluent_chat_multiple_24sp_regular_text.xml | 15 ---------- ...luent_chat_multiple_24sp_selector_text.xml | 6 ---- .../drawable/ic_fluent_share_24sp_regular.xml | 3 -- .../drawable/ic_fluent_star_24sp_filled.xml | 3 -- .../drawable/ic_fluent_star_24sp_regular.xml | 3 -- .../drawable/ic_fluent_star_24sp_selector.xml | 8 ------ .../main/res/layout/display_item_footer.xml | 28 +++++++++---------- 15 files changed, 17 insertions(+), 101 deletions(-) delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml delete mode 100644 mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml diff --git a/mastodon/src/main/res/drawable/ic_boost.xml b/mastodon/src/main/res/drawable/ic_boost.xml index a14cebaa2..64f632e5b 100644 --- a/mastodon/src/main/res/drawable/ic_boost.xml +++ b/mastodon/src/main/res/drawable/ic_boost.xml @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml deleted file mode 100644 index ee22e43a2..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_regular.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml deleted file mode 100644 index 15d5e44f6..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_24sp_very_filled.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml deleted file mode 100644 index bf315efcb..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_off_24sp_regular.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml deleted file mode 100644 index 2412d01ac..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_filled.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml deleted file mode 100644 index c32c5603b..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_regular.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml b/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml deleted file mode 100644 index 9e81bec2b..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_bookmark_24sp_selector.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml deleted file mode 100644 index 98a4373b4..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml deleted file mode 100644 index 3adbf88f5..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_regular_text.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml b/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml deleted file mode 100644 index 4e3f84614..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_chat_multiple_24sp_selector_text.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml deleted file mode 100644 index b2e6ddd0b..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_share_24sp_regular.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml deleted file mode 100644 index 92118cbfb..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_filled.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml deleted file mode 100644 index 8c946162a..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_regular.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml b/mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml deleted file mode 100644 index faf7363fa..000000000 --- a/mastodon/src/main/res/drawable/ic_fluent_star_24sp_selector.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/mastodon/src/main/res/layout/display_item_footer.xml b/mastodon/src/main/res/layout/display_item_footer.xml index 420f8b529..291a12ac2 100644 --- a/mastodon/src/main/res/layout/display_item_footer.xml +++ b/mastodon/src/main/res/layout/display_item_footer.xml @@ -24,12 +24,12 @@ android:layout_height="match_parent" android:paddingVertical="12dp"> @@ -148,11 +148,11 @@ android:paddingVertical="12dp"> From d4fbb298c19626f772846d15be18b2ba61af2c34 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 22:57:06 +0200 Subject: [PATCH 27/79] use sp for reply line inline icons --- .../displayitems/ReblogOrReplyLineStatusDisplayItem.java | 6 +++--- .../android/ui/displayitems/StatusDisplayItem.java | 6 +++--- .../drawable/ic_fluent_arrow_repeat_all_20sp_filled.xml | 7 +++++++ .../res/drawable/ic_fluent_arrow_reply_20sp_filled.xml | 7 +++++++ .../src/main/res/drawable/ic_fluent_earth_20sp_regular.xml | 7 +++++++ .../res/drawable/ic_fluent_lock_closed_20sp_filled.xml | 7 +++++++ .../main/res/drawable/ic_fluent_lock_open_20sp_regular.xml | 7 +++++++ .../res/drawable/ic_fluent_number_symbol_20sp_filled.xml | 7 +++++++ .../main/res/layout/display_item_reblog_or_reply_line.xml | 2 +- 9 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_20sp_filled.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_arrow_reply_20sp_filled.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_earth_20sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_lock_closed_20sp_filled.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_lock_open_20sp_regular.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_number_symbol_20sp_filled.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java index 696944029..03ac49430 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ReblogOrReplyLineStatusDisplayItem.java @@ -73,9 +73,9 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{ public void updateVisibility(StatusPrivacy visibility) { this.visibility = visibility; this.iconEnd = visibility != null ? switch (visibility) { - case PUBLIC -> R.drawable.ic_fluent_earth_20_regular; - case UNLISTED -> R.drawable.ic_fluent_lock_open_20_regular; - case PRIVATE -> R.drawable.ic_fluent_lock_closed_20_filled; + case PUBLIC -> R.drawable.ic_fluent_earth_20sp_regular; + case UNLISTED -> R.drawable.ic_fluent_lock_open_20sp_regular; + case PRIVATE -> R.drawable.ic_fluent_lock_closed_20sp_filled; default -> 0; } : 0; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index c55c4f925..b1bd67d3a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -129,7 +129,7 @@ public abstract class StatusDisplayItem{ : fragment.getString(R.string.in_reply_to, account.displayName); replyLine = new ReblogOrReplyLineStatusDisplayItem( parentID, fragment, text, account == null ? List.of() : account.emojis, - R.drawable.ic_fluent_arrow_reply_20_filled, null, null, fullText + R.drawable.ic_fluent_arrow_reply_20sp_filled, null, null, fullText ); } @@ -137,7 +137,7 @@ public abstract class StatusDisplayItem{ boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account); String fullText = fragment.getString(R.string.user_boosted, status.account.displayName); String text = GlobalUserPreferences.compactReblogReplyLine && replyLine != null ? status.account.displayName : fullText; - items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{ + items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20sp_filled, isOwnPost ? status.visibility : null, i->{ args.putParcelable("profileAccount", Parcels.wrap(status.account)); Nav.go(fragment.getActivity(), ProfileFragment.class, args); }, fullText)); @@ -152,7 +152,7 @@ public abstract class StatusDisplayItem{ // post contains a hashtag the user is following .ifPresent(hashtag -> items.add(new ReblogOrReplyLineStatusDisplayItem( parentID, fragment, hashtag.name, List.of(), - R.drawable.ic_fluent_number_symbol_20_filled, null, + R.drawable.ic_fluent_number_symbol_20sp_filled, null, i -> { args.putString("hashtag", hashtag.name); Nav.go(fragment.getActivity(), HashtagTimelineFragment.class, args); diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_20sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_20sp_filled.xml new file mode 100644 index 000000000..dac0ca015 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_arrow_repeat_all_20sp_filled.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_arrow_reply_20sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_arrow_reply_20sp_filled.xml new file mode 100644 index 000000000..0b67fae20 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_arrow_reply_20sp_filled.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_earth_20sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_earth_20sp_regular.xml new file mode 100644 index 000000000..ba7d44180 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_earth_20sp_regular.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_lock_closed_20sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_lock_closed_20sp_filled.xml new file mode 100644 index 000000000..e096e2ff0 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_lock_closed_20sp_filled.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_lock_open_20sp_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_lock_open_20sp_regular.xml new file mode 100644 index 000000000..6d5df216f --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_lock_open_20sp_regular.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_fluent_number_symbol_20sp_filled.xml b/mastodon/src/main/res/drawable/ic_fluent_number_symbol_20sp_filled.xml new file mode 100644 index 000000000..29a1c95e8 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_number_symbol_20sp_filled.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/display_item_reblog_or_reply_line.xml b/mastodon/src/main/res/layout/display_item_reblog_or_reply_line.xml index b15c5ae8d..c16e85f2e 100644 --- a/mastodon/src/main/res/layout/display_item_reblog_or_reply_line.xml +++ b/mastodon/src/main/res/layout/display_item_reblog_or_reply_line.xml @@ -14,7 +14,7 @@ android:paddingTop="16dp" android:paddingBottom="6dp" android:textAppearance="@style/m3_title_small" - android:drawableStart="@drawable/ic_fluent_arrow_reply_20_filled" + android:drawableStart="@drawable/ic_fluent_arrow_reply_20sp_filled" android:drawableTint="?android:textColorSecondary" android:drawablePadding="6dp" android:singleLine="true" From 433a7b15fe13d3ed45e5f8abec42a65a0c1f93e5 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 23:03:29 +0200 Subject: [PATCH 28/79] change bubble string --- mastodon/src/main/res/values/strings_sk.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index 4ccd38655..9dc3bbbd3 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -30,7 +30,7 @@ Turned off post notifications for %s Federation These are the most recent posts by the people in your federation. - These are the most recent posts by the people in your Akkoma server\'s bubble. + These are the most recent posts from the network curated by your instance admins. Megalodon %s is ready to download. Megalodon %s is downloaded and ready to install. Check for update From 14b805e883eb48e1cac94f38d80bfa8da52ac2c2 Mon Sep 17 00:00:00 2001 From: sk22 Date: Sun, 4 Jun 2023 21:13:36 +0000 Subject: [PATCH 29/79] Translated using Weblate (German) Currently translated at 100.0% (293 of 293 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/ --- .../src/main/res/values-de-rDE/strings_sk.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/mastodon/src/main/res/values-de-rDE/strings_sk.xml b/mastodon/src/main/res/values-de-rDE/strings_sk.xml index d828668c7..8f57366d8 100644 --- a/mastodon/src/main/res/values-de-rDE/strings_sk.xml +++ b/mastodon/src/main/res/values-de-rDE/strings_sk.xml @@ -274,4 +274,22 @@ Vor dem Teilen bestätigen hat reagiert hat mit %s reagiert + Mit Konto teilen + Mit Konto teilen oder öffnen + Bubble + Vorausgewählter Inhaltstyp für neue Beiträge – überschreibt den Wert, der unter „Einstellungen für Beiträge“ gesetzt ist. + Konnte nicht in der App öffnen + Nicht angegeben + Nur Text + HTML + Markdown + BBCode + MFM + Inhaltstyp + Das sind die neuesten Beiträge aus dem Netzwerk, das deine Instanz-Admins kuratiert haben. + Formatierung aktivieren + Standard-Inhaltstyp + Informationen zur Instanz momentan nicht verfügbar + In App öffnen + Dadurch lässt beim Erstellen von Beiträgen ein Inhaltstyp wie Markdown angeben. Nicht alle Instanzen unterstützen das. \ No newline at end of file From 4baaa39f356c2653e5d5a77516ea3c419f168ea7 Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 23:16:32 +0200 Subject: [PATCH 30/79] bump version --- mastodon/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 05884696a..e21b23841 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -15,8 +15,8 @@ android { applicationId "org.joinmastodon.android.sk" minSdk 23 targetSdk 33 - versionCode 89 - versionName "1.2.3+fork.89" + versionCode 90 + versionName "1.2.3+fork.90" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW'] } From 76a97fcb47dcb9500bd7c653d6314eae5e75b1a1 Mon Sep 17 00:00:00 2001 From: Grishka Date: Sun, 4 Jun 2023 23:02:48 +0300 Subject: [PATCH 31/79] Fix #591 --- .../android/fragments/ThreadFragment.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 4020cd692..d874d72b3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -357,4 +357,16 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist return Objects.hash(status, descendantNeighbor, ancestoringNeighbor); } } + + @Override + protected void onErrorRetryClick(){ + if(preloadingFailed){ + preloadingFailed=false; + V.setVisibilityAnimated(footerProgress, View.VISIBLE); + V.setVisibilityAnimated(footerError, View.GONE); + doLoadData(); + return; + } + super.onErrorRetryClick(); + } } From 1db4b1319e135b20b1b524ede632c737f668100d Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 23:26:39 +0200 Subject: [PATCH 32/79] use latest appkit version --- mastodon/build.gradle | 2 +- .../java/org/joinmastodon/android/fragments/ThreadFragment.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mastodon/build.gradle b/mastodon/build.gradle index e21b23841..6076b27c6 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -75,7 +75,7 @@ dependencies { implementation 'me.grishka.litex:dynamicanimation:1.1.0-alpha03' implementation 'me.grishka.litex:viewpager:1.0.0' implementation 'me.grishka.litex:viewpager2:1.0.0' - implementation 'me.grishka.appkit:appkit:1.2.7' + implementation 'me.grishka.appkit:appkit:1.2.8' implementation 'com.google.code.gson:gson:2.9.0' implementation 'org.jsoup:jsoup:1.14.3' implementation 'com.squareup:otto:1.3.8' diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index d874d72b3..9eeee22b8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -44,6 +44,7 @@ import java.util.stream.Collectors; import me.grishka.appkit.api.Callback; import me.grishka.appkit.api.ErrorResponse; import me.grishka.appkit.api.SimpleCallback; +import me.grishka.appkit.utils.V; public class ThreadFragment extends StatusListFragment implements ProvidesAssistContent { protected Status mainStatus, updatedStatus; From 6f25c8be0f8a3db66dbd4931eb175bb80a37cba7 Mon Sep 17 00:00:00 2001 From: Grishka Date: Sun, 4 Jun 2023 02:40:59 +0300 Subject: [PATCH 33/79] Fix #583 --- .../ui/text/ClickableLinksDelegate.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java index 414c78649..83be4b7c4 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java @@ -96,10 +96,11 @@ public class ClickableLinksDelegate { int lstart=l.getLineForOffset(start); int lend=l.getLineForOffset(end); if(line>=lstart && line<=lend){ - if(line==lstart && event.getX()-view.getPaddingLeft()l.getPrimaryHorizontal(start)))){ continue; } - if(line==lend && event.getX()-view.getPaddingLeft()>l.getPrimaryHorizontal(end)){ + if(line==lend && ((!isRTL && event.getX()-view.getPaddingLeft()>l.getPrimaryHorizontal(end)) || (isRTL && event.getX()-view.getPaddingLeft() Date: Sun, 4 Jun 2023 04:50:40 +0300 Subject: [PATCH 34/79] Fix it again --- .../ui/text/ClickableLinksDelegate.java | 86 +++++-------------- 1 file changed, 23 insertions(+), 63 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java index 83be4b7c4..46f955d6b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java @@ -4,8 +4,7 @@ import android.graphics.Canvas; import android.graphics.CornerPathEffect; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.Rect; -import android.graphics.RectF; +import android.os.Build; import android.text.Layout; import android.text.Spanned; import android.view.GestureDetector; @@ -33,7 +32,8 @@ public class ClickableLinksDelegate { hlPaint=new Paint(); hlPaint.setAntiAlias(true); hlPaint.setPathEffect(new CornerPathEffect(V.dp(3))); -// view.setHighlightColor(view.getResources().getColor(android.R.color.holo_blue_light)); + hlPaint.setStyle(Paint.Style.FILL_AND_STROKE); + hlPaint.setStrokeWidth(V.dp(4)); gestureDetector = new GestureDetector(view.getContext(), new LinkGestureListener(), view.getHandler()); } @@ -58,7 +58,7 @@ public class ClickableLinksDelegate { public void onDraw(Canvas canvas){ if(hlPath!=null){ canvas.save(); - canvas.translate(0, view.getPaddingTop()); + canvas.translate(view.getTotalPaddingLeft(), view.getTotalPaddingTop()); canvas.drawPath(hlPath, hlPaint); canvas.restore(); } @@ -73,69 +73,29 @@ public class ClickableLinksDelegate { private class LinkGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(@NonNull MotionEvent event) { - int line=-1; - Rect rect=new Rect(); - Layout l=view.getLayout(); - for(int i=0;iview.getWidth()-padRight || y>view.getHeight()-padBottom) return false; - } + x-=padLeft; + y-=padTop; + Layout l=view.getLayout(); + int line=l.getLineForVertical(Math.round(y)); + int position=l.getOffsetForHorizontal(line, x); + CharSequence text=view.getText(); if(text instanceof Spanned s){ LinkSpan[] spans=s.getSpans(0, s.length()-1, LinkSpan.class); - if(spans.length>0){ - for(LinkSpan span:spans){ - int start=s.getSpanStart(span); - int end=s.getSpanEnd(span); - int lstart=l.getLineForOffset(start); - int lend=l.getLineForOffset(end); - if(line>=lstart && line<=lend){ - boolean isRTL=l.getParagraphDirection(line)==-1; - if(line==lstart && ((!isRTL && event.getX()-view.getPaddingLeft()l.getPrimaryHorizontal(start)))){ - continue; - } - if(line==lend && ((!isRTL && event.getX()-view.getPaddingLeft()>l.getPrimaryHorizontal(end)) || (isRTL && event.getX()-view.getPaddingLeft()position){ + selectedSpan=span; + hlPath=new Path(); + l.getSelectionPath(start, end, hlPath); + hlPaint.setColor((span.getColor() & 0x00FFFFFF) | 0x33000000); + view.invalidate(); + return true; } } } From 853124e2ce0c7ffa0115c3b72a546038fc1fc0ff Mon Sep 17 00:00:00 2001 From: Grishka Date: Sun, 4 Jun 2023 04:50:40 +0300 Subject: [PATCH 35/79] Fix it again --- .../joinmastodon/android/ui/text/ClickableLinksDelegate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java index 46f955d6b..39a6e589f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java @@ -31,9 +31,9 @@ public class ClickableLinksDelegate { this.view=view; hlPaint=new Paint(); hlPaint.setAntiAlias(true); - hlPaint.setPathEffect(new CornerPathEffect(V.dp(3))); + hlPaint.setPathEffect(new CornerPathEffect(dp(3))); hlPaint.setStyle(Paint.Style.FILL_AND_STROKE); - hlPaint.setStrokeWidth(V.dp(4)); + hlPaint.setStrokeWidth(dp(4)); gestureDetector = new GestureDetector(view.getContext(), new LinkGestureListener(), view.getHandler()); } From 0665b8dd3b7b339a72f461e93afccea0ecda632e Mon Sep 17 00:00:00 2001 From: sk Date: Sun, 4 Jun 2023 23:33:04 +0200 Subject: [PATCH 36/79] fix incompatibility with upstream bugfix --- .../joinmastodon/android/ui/text/ClickableLinksDelegate.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java index 39a6e589f..46f955d6b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/ClickableLinksDelegate.java @@ -31,9 +31,9 @@ public class ClickableLinksDelegate { this.view=view; hlPaint=new Paint(); hlPaint.setAntiAlias(true); - hlPaint.setPathEffect(new CornerPathEffect(dp(3))); + hlPaint.setPathEffect(new CornerPathEffect(V.dp(3))); hlPaint.setStyle(Paint.Style.FILL_AND_STROKE); - hlPaint.setStrokeWidth(dp(4)); + hlPaint.setStrokeWidth(V.dp(4)); gestureDetector = new GestureDetector(view.getContext(), new LinkGestureListener(), view.getHandler()); } From 1b4579346b3f72bde1de1678e4e2f1c37fc75328 Mon Sep 17 00:00:00 2001 From: Grishka Date: Sun, 4 Jun 2023 02:11:40 +0300 Subject: [PATCH 37/79] Fix #548 --- .../android/fragments/StatusListFragment.java | 2 +- .../org/joinmastodon/android/model/BaseModel.java | 13 ++++++++++++- .../android/model/PushSubscription.java | 5 +---- .../java/org/joinmastodon/android/model/Status.java | 11 ++++++++++- .../org/joinmastodon/android/ui/utils/UiUtils.java | 2 +- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java index bf22056b3..838ace548 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java @@ -68,7 +68,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment status.filterRevealed = true; Bundle args=new Bundle(); args.putString("account", accountID); - args.putParcelable("status", Parcels.wrap(status)); + args.putParcelable("status", Parcels.wrap(status.clone())); if(status.inReplyToAccountId!=null && knownAccounts.containsKey(status.inReplyToAccountId)) args.putParcelable("inReplyToAccount", Parcels.wrap(knownAccounts.get(status.inReplyToAccountId))); Nav.go(getActivity(), ThreadFragment.class, args); diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java b/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java index e68026c9e..b2ec8898f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java @@ -8,8 +8,9 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import androidx.annotation.CallSuper; +import androidx.annotation.NonNull; -public abstract class BaseModel{ +public abstract class BaseModel implements Cloneable{ @CallSuper public void postprocess() throws ObjectValidationException{ try{ @@ -23,4 +24,14 @@ public abstract class BaseModel{ } }catch(IllegalAccessException ignore){} } + + @NonNull + @Override + public Object clone(){ + try{ + return super.clone(); + }catch(CloneNotSupportedException x){ + throw new RuntimeException(x); + } + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java b/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java index 7d9edd1d4..f3c4f4fa8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/PushSubscription.java @@ -30,10 +30,7 @@ public class PushSubscription extends BaseModel implements Cloneable{ @NonNull @Override public PushSubscription clone(){ - PushSubscription copy=null; - try{ - copy=(PushSubscription) super.clone(); - }catch(CloneNotSupportedException ignore){} + PushSubscription copy=(PushSubscription) super.clone(); copy.alerts=alerts.clone(); return copy; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java index 8df45b976..197ee70b6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -20,6 +20,8 @@ import java.lang.reflect.Type; import java.time.Instant; import java.util.List; +import androidx.annotation.NonNull; + @Parcel public class Status extends BaseModel implements DisplayItemsParent, Searchable{ @RequiredField @@ -190,8 +192,15 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ return url; } - public static class StatusDeserializer implements JsonDeserializer { + @NonNull + @Override + public Status clone(){ + Status copy=(Status) super.clone(); + copy.spoilerRevealed=false; + return copy; + } + public static class StatusDeserializer implements JsonDeserializer { @Override public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject obj = json.getAsJsonObject(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 0988e589b..0c69a5a7d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -1395,7 +1395,7 @@ public class UiUtils { extras.putString("account", accountID); if (n.status!=null) { Status status=n.status; - extras.putParcelable("status", Parcels.wrap(status)); + extras.putParcelable("status", Parcels.wrap(status.clone())); Nav.go((Activity) context, ThreadFragment.class, extras); } else if (n.report != null) { String domain = AccountSessionManager.getInstance().getAccount(accountID).domain; From d4a52868952548bb5f41f2988dbe62c97e378a99 Mon Sep 17 00:00:00 2001 From: Grishka Date: Tue, 23 May 2023 12:56:49 +0300 Subject: [PATCH 38/79] Fix #553 --- .../android/api/CacheController.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java index 335d00914..f21f9e622 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java @@ -36,7 +36,7 @@ import me.grishka.appkit.utils.WorkerThread; public class CacheController{ private static final String TAG="CacheController"; - private static final int DB_VERSION=3; + private static final int DB_VERSION=4; private static final WorkerThread databaseThread=new WorkerThread("databaseThread"); private static final Handler uiHandler=new Handler(Looper.getMainLooper()); @@ -61,7 +61,7 @@ public class CacheController{ List filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.HOME)).collect(Collectors.toList()); if(!forceReload){ SQLiteDatabase db=getOrOpenDatabase(); - try(Cursor cursor=db.query("home_timeline", new String[]{"json", "flags"}, maxID==null ? null : "`id` result=new ArrayList<>(); cursor.moveToFirst(); @@ -112,7 +112,7 @@ public class CacheController{ runOnDbThread((db)->{ if(clear) db.delete("home_timeline", null, null); - ContentValues values=new ContentValues(3); + ContentValues values=new ContentValues(4); for(Status s:posts){ values.put("id", s.id); values.put("json", MastodonAPIController.gson.toJson(s)); @@ -120,6 +120,7 @@ public class CacheController{ if(s.hasGapAfter) flags|=POST_FLAG_GAP_AFTER; values.put("flags", flags); + values.put("time", s.createdAt.getEpochSecond()); db.insertWithOnConflict("home_timeline", null, values, SQLiteDatabase.CONFLICT_REPLACE); } }); @@ -134,7 +135,7 @@ public class CacheController{ if(!forceReload){ SQLiteDatabase db=getOrOpenDatabase(); String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all"; - try(Cursor cursor=db.query(table, new String[]{"json"}, maxID==null ? null : "`id` result=new ArrayList<>(); cursor.moveToFirst(); @@ -192,7 +193,7 @@ public class CacheController{ String table=onlyPosts ? "notifications_posts" : onlyMentions ? "notifications_mentions" : "notifications_all"; if(clear) db.delete(table, null, null); - ContentValues values=new ContentValues(3); + ContentValues values=new ContentValues(4); for(Notification n:notifications){ if(n.type==null){ continue; @@ -200,6 +201,7 @@ public class CacheController{ values.put("id", n.id); values.put("json", MastodonAPIController.gson.toJson(n)); values.put("type", n.type.ordinal()); + values.put("time", n.createdAt.getEpochSecond()); db.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_REPLACE); } }); @@ -296,21 +298,24 @@ public class CacheController{ CREATE TABLE `home_timeline` ( `id` VARCHAR(25) NOT NULL PRIMARY KEY, `json` TEXT NOT NULL, - `flags` INTEGER NOT NULL DEFAULT 0 + `flags` INTEGER NOT NULL DEFAULT 0, + `time` INTEGER NOT NULL )"""); db.execSQL(""" CREATE TABLE `notifications_all` ( `id` VARCHAR(25) NOT NULL PRIMARY KEY, `json` TEXT NOT NULL, `flags` INTEGER NOT NULL DEFAULT 0, - `type` INTEGER NOT NULL + `type` INTEGER NOT NULL, + `time` INTEGER NOT NULL )"""); db.execSQL(""" CREATE TABLE `notifications_mentions` ( `id` VARCHAR(25) NOT NULL PRIMARY KEY, `json` TEXT NOT NULL, `flags` INTEGER NOT NULL DEFAULT 0, - `type` INTEGER NOT NULL + `type` INTEGER NOT NULL, + `time` INTEGER NOT NULL )"""); createRecentSearchesTable(db); createPostsNotificationsTable(db); @@ -318,12 +323,16 @@ public class CacheController{ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){ - if(oldVersion==1){ + if(oldVersion<2){ createRecentSearchesTable(db); } - if(oldVersion==2){ + if(oldVersion<3){ + // MEGALODON-SPECIFIC createPostsNotificationsTable(db); } + if(oldVersion<4){ + addTimeColumns(db); + } } private void createRecentSearchesTable(SQLiteDatabase db){ @@ -344,6 +353,15 @@ public class CacheController{ `type` INTEGER NOT NULL )"""); } + + private void addTimeColumns(SQLiteDatabase db){ + db.execSQL("DELETE FROM `home_timeline`"); + db.execSQL("DELETE FROM `notifications_all`"); + db.execSQL("DELETE FROM `notifications_mentions`"); + db.execSQL("ALTER TABLE `home_timeline` ADD `time` INTEGER NOT NULL DEFAULT 0"); + db.execSQL("ALTER TABLE `notifications_all` ADD `time` INTEGER NOT NULL DEFAULT 0"); + db.execSQL("ALTER TABLE `notifications_mentions` ADD `time` INTEGER NOT NULL DEFAULT 0"); + } } @FunctionalInterface From 4673a4b9f71a81792774c96c455aeb0274a69021 Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 5 Jun 2023 11:22:01 +0200 Subject: [PATCH 39/79] add missing database column in post notification table --- .../java/org/joinmastodon/android/api/CacheController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java index f21f9e622..3cab9c71f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java @@ -350,7 +350,8 @@ public class CacheController{ `id` VARCHAR(25) NOT NULL PRIMARY KEY, `json` TEXT NOT NULL, `flags` INTEGER NOT NULL DEFAULT 0, - `type` INTEGER NOT NULL + `type` INTEGER NOT NULL, + `time` INTEGER NOT NULL )"""); } @@ -358,9 +359,11 @@ public class CacheController{ db.execSQL("DELETE FROM `home_timeline`"); db.execSQL("DELETE FROM `notifications_all`"); db.execSQL("DELETE FROM `notifications_mentions`"); + db.execSQL("DELETE FROM `notifications_posts`"); db.execSQL("ALTER TABLE `home_timeline` ADD `time` INTEGER NOT NULL DEFAULT 0"); db.execSQL("ALTER TABLE `notifications_all` ADD `time` INTEGER NOT NULL DEFAULT 0"); db.execSQL("ALTER TABLE `notifications_mentions` ADD `time` INTEGER NOT NULL DEFAULT 0"); + db.execSQL("ALTER TABLE `notifications_posts` ADD `time` INTEGER NOT NULL DEFAULT 0"); } } From a9c2df2e83e2dd8d071b6f8d211eabe7ab4c50ae Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 5 Jun 2023 11:26:36 +0200 Subject: [PATCH 40/79] do copy spoilerRevealed on clone closes sk22#547 --- .../main/java/org/joinmastodon/android/model/Status.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java index 197ee70b6..1849b5b07 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -192,14 +192,6 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ return url; } - @NonNull - @Override - public Status clone(){ - Status copy=(Status) super.clone(); - copy.spoilerRevealed=false; - return copy; - } - public static class StatusDeserializer implements JsonDeserializer { @Override public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { From d16e199dd12a4f96aa74d9c9d77082627f44a307 Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 5 Jun 2023 11:48:40 +0200 Subject: [PATCH 41/79] scroll up when posting on profile fragment closes sk22#546 --- .../android/fragments/AccountTimelineFragment.java | 4 +++- .../android/fragments/BaseStatusListFragment.java | 7 ++++++- .../joinmastodon/android/fragments/ProfileFragment.java | 1 - .../android/fragments/discover/DiscoverPostsFragment.java | 8 +------- .../android/fragments/discover/SearchFragment.java | 8 +------- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java index 1db365282..67cd4df72 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java @@ -20,6 +20,7 @@ import org.parceler.Parcels; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import me.grishka.appkit.api.SimpleCallback; @@ -95,10 +96,11 @@ public class AccountTimelineFragment extends StatusListFragment{ if(ev.status.inReplyToAccountId!=null && !ev.status.inReplyToAccountId.equals(AccountSessionManager.getInstance().getAccount(accountID).self.id)) return; }else if(filter==GetAccountStatuses.Filter.MEDIA){ - if(ev.status.mediaAttachments.isEmpty()) + if(Optional.ofNullable(ev.status.mediaAttachments).map(List::isEmpty).orElse(true)) return; } prependItems(Collections.singletonList(ev.status), true); + if (isOnTop()) scrollToTop(); } protected void onStatusUnpinned(StatusUnpinnedEvent ev){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 96480351e..5ae8a2d0f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -71,7 +71,7 @@ import me.grishka.appkit.utils.BindableViewHolder; import me.grishka.appkit.utils.V; import me.grishka.appkit.views.UsableRecyclerView; -public abstract class BaseStatusListFragment extends RecyclerFragment implements PhotoViewerHost, ScrollableToTop, HasFab, ProvidesAssistContent.ProvidesWebUri { +public abstract class BaseStatusListFragment extends RecyclerFragment implements PhotoViewerHost, ScrollableToTop, IsOnTop, HasFab, ProvidesAssistContent.ProvidesWebUri { protected ArrayList displayItems=new ArrayList<>(); protected DisplayItemsAdapter adapter; protected String accountID; @@ -680,6 +680,11 @@ public abstract class BaseStatusListFragment exten smoothScrollRecyclerViewToTop(list); } + @Override + public boolean isOnTop() { + return isRecyclerViewOnTop(list); + } + protected int getListWidthForMediaLayout(){ return list.getWidth(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 9c706a3e7..d8ccc4c30 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -58,7 +58,6 @@ import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.AccountField; import org.joinmastodon.android.model.Attachment; -import org.joinmastodon.android.model.Instance; import org.joinmastodon.android.model.Relationship; import org.joinmastodon.android.ui.BetterItemAnimator; import org.joinmastodon.android.ui.SimpleViewHolder; diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java index e6d7c4a4c..4130b16a3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java @@ -5,7 +5,6 @@ import android.os.Bundle; import android.view.View; import org.joinmastodon.android.api.requests.trends.GetTrendingStatuses; -import org.joinmastodon.android.fragments.IsOnTop; import org.joinmastodon.android.fragments.StatusListFragment; import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Status; @@ -17,7 +16,7 @@ import java.util.stream.Collectors; import me.grishka.appkit.api.SimpleCallback; -public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop { +public class DiscoverPostsFragment extends StatusListFragment { private DiscoverInfoBannerHelper bannerHelper=new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.TRENDING_POSTS); @Override @@ -39,11 +38,6 @@ public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop bannerHelper.maybeAddBanner(contentWrap); } - @Override - public boolean isOnTop() { - return isRecyclerViewOnTop(list); - } - @Override protected Filter.FilterContext getFilterContext() { return Filter.FilterContext.PUBLIC; diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java index e4a375e83..741149c35 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java @@ -12,7 +12,6 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.search.GetSearchResults; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.fragments.BaseStatusListFragment; -import org.joinmastodon.android.fragments.IsOnTop; import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.fragments.ThreadFragment; import org.joinmastodon.android.model.Account; @@ -43,7 +42,7 @@ import me.grishka.appkit.api.ErrorResponse; import me.grishka.appkit.utils.MergeRecyclerAdapter; import me.grishka.appkit.utils.V; -public class SearchFragment extends BaseStatusListFragment implements IsOnTop { +public class SearchFragment extends BaseStatusListFragment { private String currentQuery; private List prevDisplayItems; private EnumSet currentFilter=EnumSet.allOf(SearchResult.Type.class); @@ -312,11 +311,6 @@ public class SearchFragment extends BaseStatusListFragment impleme } } - @Override - public boolean isOnTop() { - return isRecyclerViewOnTop(list); - } - @Override public Uri getWebUri(Uri.Builder base) { Uri.Builder searchUri = base.path("/search"); From bc9bec3d665b41914bc0c2aa11623700a55fe596 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 5 Jun 2023 12:47:38 +0200 Subject: [PATCH 42/79] New translations strings.xml (Swedish) --- mastodon/src/main/res/values-sv-rSE/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/res/values-sv-rSE/strings.xml b/mastodon/src/main/res/values-sv-rSE/strings.xml index c69fe4787..d1e47cb6b 100644 --- a/mastodon/src/main/res/values-sv-rSE/strings.xml +++ b/mastodon/src/main/res/values-sv-rSE/strings.xml @@ -413,6 +413,7 @@ %1$s tillåter inte registrering från %2$s. Prova en annan eller <a>välj en annan server</a>. Det här användarnamnet är redan taget. Spara ändringar + Tidslinje eller Välkommen till Mastodon From ef6238b593fbdd4aa966b26ee86cb796323532ad Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 5 Jun 2023 13:45:02 +0200 Subject: [PATCH 43/79] New translations strings.xml (Swedish) --- mastodon/src/main/res/values-sv-rSE/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mastodon/src/main/res/values-sv-rSE/strings.xml b/mastodon/src/main/res/values-sv-rSE/strings.xml index d1e47cb6b..e437a3f9f 100644 --- a/mastodon/src/main/res/values-sv-rSE/strings.xml +++ b/mastodon/src/main/res/values-sv-rSE/strings.xml @@ -10,11 +10,13 @@ OK Förbereder för autentisering… Slutför autentisering… + %s boostade Som svar på %s Notiser följde dig skickade en förfrågning om att följa till dig favoritmarkerade dit inlägg + boostade ditt inlägg omröstning avslutad %ds %dm @@ -412,8 +414,14 @@ %1$s tillåter inte registrering från %2$s. Prova en annan eller <a>välj en annan server</a>. Det här användarnamnet är redan taget. + Visa ändå Spara ändringar Tidslinje + Visa alla + Konton + Verifierad länk + Visa eller Välkommen till Mastodon + Vad är servrar? From 3d5fb2dfea437b3fc8741281a30896edad6c50b3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 5 Jun 2023 15:00:47 +0200 Subject: [PATCH 44/79] New translations strings.xml (Swedish) --- mastodon/src/main/res/values-sv-rSE/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/res/values-sv-rSE/strings.xml b/mastodon/src/main/res/values-sv-rSE/strings.xml index e437a3f9f..e4209c1ab 100644 --- a/mastodon/src/main/res/values-sv-rSE/strings.xml +++ b/mastodon/src/main/res/values-sv-rSE/strings.xml @@ -166,6 +166,7 @@ Medan vi granskar detta kan du vidta åtgärder mot %s. Avfölj %s Avfölj + Du kommer inte att se deras inlägg eller boosts i ditt hemflöde. De kommer inte veta att de har blivit tystade. De kommer inte längre att kunna följa eller se dina inlägg, men de kan se om de har blockerats. Vill du inte se det här? När du ser något som du inte gillar på Mastodon kan du ta bort personen från din upplevelse. From 8c4678aba57623b268675884d42f09009a413bdd Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 5 Jun 2023 16:05:58 +0200 Subject: [PATCH 45/79] fix updated main status not being applied --- .../android/fragments/ThreadFragmentTest.java | 12 +++++-- .../android/fragments/ThreadFragment.java | 32 ++++++++++++------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java b/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java index 4e17aceb4..38a2a489c 100644 --- a/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java +++ b/mastodon/src/androidTest/java/org/joinmastodon/android/fragments/ThreadFragmentTest.java @@ -53,24 +53,30 @@ public class ThreadFragmentTest { } @Test - public void updateMainStatus() { + public void maybeApplyMainStatus() { ThreadFragment fragment = new ThreadFragment(); + fragment.contextInitiallyRendered = true; fragment.mainStatus = Status.ofFake("123456", "original text", Instant.EPOCH); Status update1 = Status.ofFake("123456", "updated text", Instant.EPOCH); update1.editedAt = Instant.ofEpochSecond(1); fragment.updatedStatus = update1; - StatusUpdatedEvent event1 = (StatusUpdatedEvent) fragment.updateMainStatus(); + StatusUpdatedEvent event1 = (StatusUpdatedEvent) fragment.maybeApplyMainStatus(); assertEquals("fired update event", update1, event1.status); assertEquals("updated main status", update1, fragment.mainStatus); Status update2 = Status.ofFake("123456", "updated text", Instant.EPOCH); update2.favouritesCount = 123; fragment.updatedStatus = update2; - StatusCountersUpdatedEvent event2 = (StatusCountersUpdatedEvent) fragment.updateMainStatus(); + StatusCountersUpdatedEvent event2 = (StatusCountersUpdatedEvent) fragment.maybeApplyMainStatus(); assertEquals("only fired counter update event", update2.id, event2.id); assertEquals("updated counter is correct", 123, event2.favorites); assertEquals("updated main status", update2, fragment.mainStatus); + + Status update3 = Status.ofFake("123456", "whatever", Instant.EPOCH); + fragment.contextInitiallyRendered = false; + fragment.updatedStatus = update3; + assertNull("no update when context hasn't been rendered", fragment.maybeApplyMainStatus()); } @Test diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 9eeee22b8..15ebb3de0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -49,7 +49,7 @@ import me.grishka.appkit.utils.V; public class ThreadFragment extends StatusListFragment implements ProvidesAssistContent { protected Status mainStatus, updatedStatus; private final HashMap ancestryMap = new HashMap<>(); - private boolean initialAnimationFinished; + protected boolean contextInitiallyRendered; @Override public void onCreate(Bundle savedInstanceState){ @@ -111,7 +111,7 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist @Override protected void doLoadData(int offset, int count){ - refreshMainStatus(); + loadMainStatus(); currentRequest=new GetStatusContext(mainStatus.id) .setCallback(new SimpleCallback<>(this){ @Override @@ -155,21 +155,28 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist adapter.notifyDataSetChanged(); } list.scrollToPosition(displayItems.size()-count); + + // no animation is going to happen, so proceeding to apply right now + if (data.size() == 1) { + contextInitiallyRendered = true; + // for the case that the main status has already finished loading + maybeApplyMainStatus(); + } } }) .exec(accountID); } - private void refreshMainStatus() { + private void loadMainStatus() { new GetStatusByID(mainStatus.id) .setCallback(new Callback<>() { @Override public void onSuccess(Status status) { if (getContext() == null || status == null) return; updatedStatus = status; - // only update main status if the initial animation is already finished. - // otherwise, the animator will call it in onAnimationFinished - if (initialAnimationFinished || data.size() == 1) updateMainStatus(); + // for the case that the context has already loaded (and the animation has + // already finished), falling back to applying it ourselves: + maybeApplyMainStatus(); } @Override @@ -177,7 +184,9 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist }).exec(accountID); } - protected Object updateMainStatus() { + protected Object maybeApplyMainStatus() { + if (updatedStatus == null || !contextInitiallyRendered) return null; + // returning fired event object to facilitate testing Object event; if (updatedStatus.editedAt != null && @@ -288,15 +297,14 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist showContent(); if(!loaded) footerProgress.setVisibility(View.VISIBLE); + list.setItemAnimator(new BetterItemAnimator() { @Override public void onAnimationFinished(@NonNull RecyclerView.ViewHolder viewHolder) { super.onAnimationFinished(viewHolder); - // in case someone else is about to call updateMainStatus faster... - initialAnimationFinished = true; - // ...if not (someone did fetch it but the animation wasn't finished yet), - // call it now - if (updatedStatus != null) updateMainStatus(); + contextInitiallyRendered = true; + // for the case that both requests are already done (and thus won't apply it) + maybeApplyMainStatus(); } }); } From ed994b23e92938831752890d01f5d2a4c41ab89e Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 5 Jun 2023 16:12:07 +0200 Subject: [PATCH 46/79] fix wrong views' states being modified closes sk22#549 --- .../displayitems/FooterStatusDisplayItem.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java index a0dbdcc05..93facae14 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java @@ -5,7 +5,6 @@ import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -17,7 +16,6 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.widget.Button; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.TextView; import org.joinmastodon.android.GlobalUserPreferences; @@ -125,9 +123,9 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ @Override public void onBind(FooterStatusDisplayItem item){ - bindButton(replies, item.status.repliesCount); - bindButton(boosts, item.status.reblogsCount); - bindButton(favorites, item.status.favouritesCount); + bindText(replies, item.status.repliesCount); + bindText(boosts, item.status.reblogsCount); + bindText(favorites, item.status.favouritesCount); // in thread view, direct descendant posts display one direct reply to themselves, // hence in that case displaying whether there is another reply int compareTo = item.isMainStatus || !item.hasDescendantNeighbor ? 0 : 1; @@ -151,7 +149,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ itemView.requestLayout(); } - private void bindButton(TextView btn, long count){ + private void bindText(TextView btn, long count){ if(GlobalUserPreferences.showInteractionCounts && count>0 && !item.hideCounts){ btn.setText(UiUtils.abbreviateNumber(count)); btn.setCompoundDrawablePadding(V.dp(8)); @@ -215,13 +213,13 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ onBoostLongClick(v); return; } - boosts.setSelected(!item.status.reblogged); + boost.setSelected(!item.status.reblogged); AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setReblogged(item.status, !item.status.reblogged, null, r->boostConsumer(v, r)); } private void boostConsumer(View v, Status r) { v.startAnimation(opacityIn); - bindButton(boosts, r.reblogsCount); + bindText(boosts, r.reblogsCount); } private boolean onBoostLongClick(View v){ @@ -307,10 +305,10 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ } private void onFavoriteClick(View v){ - favorites.setSelected(!item.status.favourited); + favorite.setSelected(!item.status.favourited); AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{ v.startAnimation(opacityIn); - bindButton(favorites, r.favouritesCount); + bindText(favorites, r.favouritesCount); }); } From 3d24b2de101437c55252b21cfb2e1bc29e97fa57 Mon Sep 17 00:00:00 2001 From: sk Date: Mon, 5 Jun 2023 16:19:55 +0200 Subject: [PATCH 47/79] add status counters event listener to notifications --- .../fragments/NotificationsListFragment.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java index 6650e6752..c01d88f15 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java @@ -16,6 +16,7 @@ import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.events.AllNotificationsSeenEvent; import org.joinmastodon.android.events.PollUpdatedEvent; import org.joinmastodon.android.events.RemoveAccountPostsEvent; +import org.joinmastodon.android.events.StatusCountersUpdatedEvent; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.CacheablePaginatedResponse; import org.joinmastodon.android.model.Emoji; @@ -25,6 +26,8 @@ import org.joinmastodon.android.model.Markers; import org.joinmastodon.android.model.Notification; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem; +import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.StatusDisplayItem; import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem; @@ -227,6 +230,32 @@ public class NotificationsListFragment extends BaseStatusListFragment Date: Mon, 5 Jun 2023 18:07:22 +0200 Subject: [PATCH 48/79] fix badged toolbar icons with new appkit version --- .../android/fragments/MastodonToolbarFragment.java | 5 +++++ .../src/main/res/drawable/ic_announcements_24_badged.xml | 2 +- .../src/main/res/drawable/ic_follow_requests_24_badged.xml | 2 +- mastodon/src/main/res/drawable/ic_settings_24_badged.xml | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/MastodonToolbarFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/MastodonToolbarFragment.java index 93cb88fdf..f690f1a39 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/MastodonToolbarFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/MastodonToolbarFragment.java @@ -30,4 +30,9 @@ public abstract class MastodonToolbarFragment extends ToolbarFragment{ toolbar.setNavigationContentDescription(R.string.back); } } + + @Override + protected boolean wantsToolbarMenuIconsTinted() { + return false; // else, badged icons don't work :( + } } diff --git a/mastodon/src/main/res/drawable/ic_announcements_24_badged.xml b/mastodon/src/main/res/drawable/ic_announcements_24_badged.xml index e5b51e160..1f767581d 100644 --- a/mastodon/src/main/res/drawable/ic_announcements_24_badged.xml +++ b/mastodon/src/main/res/drawable/ic_announcements_24_badged.xml @@ -3,7 +3,7 @@ - + diff --git a/mastodon/src/main/res/drawable/ic_follow_requests_24_badged.xml b/mastodon/src/main/res/drawable/ic_follow_requests_24_badged.xml index 7f9048281..1eca85d57 100644 --- a/mastodon/src/main/res/drawable/ic_follow_requests_24_badged.xml +++ b/mastodon/src/main/res/drawable/ic_follow_requests_24_badged.xml @@ -3,7 +3,7 @@ - + diff --git a/mastodon/src/main/res/drawable/ic_settings_24_badged.xml b/mastodon/src/main/res/drawable/ic_settings_24_badged.xml index 98a6358e3..9a5ddcf48 100644 --- a/mastodon/src/main/res/drawable/ic_settings_24_badged.xml +++ b/mastodon/src/main/res/drawable/ic_settings_24_badged.xml @@ -3,7 +3,7 @@ - + From 428881413847d1d5677a1972420e5827fce46ac0 Mon Sep 17 00:00:00 2001 From: ihor_ck Date: Sun, 4 Jun 2023 22:15:09 +0000 Subject: [PATCH 49/79] Translated using Weblate (Ukrainian) Currently translated at 100.0% (293 of 293 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/ --- mastodon/src/main/res/values-uk-rUA/strings_sk.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/res/values-uk-rUA/strings_sk.xml b/mastodon/src/main/res/values-uk-rUA/strings_sk.xml index 58eb54971..95cd0823c 100644 --- a/mastodon/src/main/res/values-uk-rUA/strings_sk.xml +++ b/mastodon/src/main/res/values-uk-rUA/strings_sk.xml @@ -288,8 +288,9 @@ Дозволяє налаштувати тип вмісту, наприклад, Markdown, під час написання допису. Зауважте, що не всі сервери підтримують цю функцію. Відкрити у застосунку Поділитися через обліковий запис - Це найновіші дописи людей у бульбашці вашого сервера Akkoma. + Це найновіші дописи з мережі керованої адміністраторами вашого сервера. Бульбашка Сервер тимчасово недоступний Поділитися або відкрити за допомогою облікового запису + Не вдалося відкрити в застосунку \ No newline at end of file From 47eeb01b7526f796c160fc26017d6894105fa994 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 5 Jun 2023 21:08:28 +0200 Subject: [PATCH 50/79] New translations strings.xml (Turkish) --- .../src/main/res/values-tr-rTR/strings.xml | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/mastodon/src/main/res/values-tr-rTR/strings.xml b/mastodon/src/main/res/values-tr-rTR/strings.xml index 200af3542..27204032f 100644 --- a/mastodon/src/main/res/values-tr-rTR/strings.xml +++ b/mastodon/src/main/res/values-tr-rTR/strings.xml @@ -6,17 +6,17 @@ Sonraki Sunucu bilgisi alınıyor… Hata - %s bir Mastodon sunucusu gibi görünmüyor. + %s bir Mastodon sunucusu gibi görükmüyor. Tamam Kimlik doğrulama için hazırlanıyor… Kimlik doğrulama tamamlanıyor… - %s yineledi + %s paylaştı %s için yanıt Bildirimler sizi takip etti sana bir takip isteği gönderdi gönderinizi favorilerine ekledi - gönderinizi yineledi + gönderinizi paylaştı oylama sona erdi %ds %ddk @@ -123,7 +123,7 @@ Sil Gönderiyi sil Bu gönderiyi silmek istediğinizden emin misiniz? - Siliniyor... + Siliniyor Ses çal Oynat Durdur @@ -149,7 +149,7 @@ En iyi eşleşmeyi seçin Hoşuma gitmiyor Görmek isteyeceğin bir şey değil - Bu spam + Spam Kötü amaçlı bağlantılar, sahte etkileşim veya tekrarlayan yanıtlar Sunucu kurallarını ihlal ediyor Belirli kuralları çiğnediğinin farkındasınız @@ -166,7 +166,7 @@ Biz bunu incelerken siz %s karşı önlem alabilirsiniz. Takipten çık %s Takipten çık - Ana sayfa akışınızda kişinin gönderilerini görmeyeceksiniz. Sessize alındıklarını bilemeyecekler. + Anasayfa akışınızda kişinin gönderilerini görmeyeceksiniz. Sessize alındıklarını bilemeyecekler. Artık sizi takip edemez ve gönderilerinizi göremezler ama engellendiklerini görebilirler. Bunu görmek istemiyor musun? Mastodon\'da beğenmediğiniz bir şey gördüğünüzde, o kişiyi deneyiminizden çıkarabilirsiniz. @@ -200,10 +200,10 @@ E-posta Kutunuzu Kontrol Edin %s işleminizi doğrulamak için size gönderdiğimiz linke tıklayınız. Biz burada bekleyeceğiz. - Link size ulaşmadı mı? + Bağlantı size ulaşmadı mı? Yeniden gönder - E-posta uygulamasını aç - Onay e-postası gönderildi + Eposta uygulamasını aç + Onay epostası gönderildi Aklınızdan geçenleri yazın veya yapıştırın İçerik Uyarısı Resim açıklaması ekle… @@ -243,18 +243,18 @@ Animasyonlu avatarları ve emojileri oynat Uygulama içi tarayıcıyı kullan Bildirimler - Beni şu durumda bilgilendir: - Herhangi biri + Beni şu durumda bilgilendir + Herhangibiri Bir takipçim Takip ettiğim biri Bilgilendirme Gönderimi favorilerine eklediğinde Beni takip ettiğinde - Gönderimi yinelediğinde + Gönderimi paylaştığında Benden bahsettiğinde Sıkıcı bölge Hesap ayarları - Mastodon\'a katkıda bulunun + Mastodona katkıda bulunun Kullanım Şartları Gizlilik Politikası Tehlikeli bölge @@ -271,7 +271,7 @@ İçeriği gizle Yeni gönderi Cevapla - Yinele + Yeniden Paylaş Favorile Paylaş Açıklamasız medya @@ -282,7 +282,7 @@ Anasayfa Profilim Medya görüntüleyici - %s\'yi takip et + %s \'yi takip et %s takip edilmedi %s kişisini takip ediyorsunuz %s takip isteği gönderdi @@ -310,11 +310,11 @@ Bu gönderiler seninle aynı Mastodon sunucusunda olan kişilerin paylaştığı son gönderilerdir. Yoksay Yeni gönderileri gör - Daha fazla gönderi yükle - Geri Takip Et + Daha fazlası + Takip edeni Takip Et Bekliyor Seni takip ediyor - Takipçileri manuel kabul eder + Takipçileri elle kabul et Kullanılan hesap %s oturumunu kapat @@ -331,8 +331,8 @@ %,d favori - %,d yineleme - %,d yineleme + %,d paylaşma + %,d paylaşma %1$s tarihinde %2$s uygulamasıyla şimdi @@ -361,7 +361,7 @@ Medya eklendi Medya kaldırıldı Medya düzenlendi - Hassas olarak işarlendi + Hassas olarak işaretlendi Hassas değil olarak işaretlendi Gönderi düzenlendi Düzenle @@ -373,16 +373,16 @@ %.2f GB %2$s dosyadan %1$s %s kaldı - Cihazınızın internet bağlantısı koptu + Cihazınızın ağ bağlantısı koptu İşleniyor… - Mastodon Android uygulamasının %s versiyonu indirmeye hazır. + Mastodon, Android uygulamasının %s versiyonu indirmeye hazır. - Mastodon Android uygulamasının %s versiyonu indirildi ve kurulmaya hazır. + Mastodon , Android uygulamasının %s versiyonu indirildi ve kurulmaya hazır. İndir (%s) Kur - Gizliliğiniz + Gizliliğiniz. Mastodon uygulaması herhangi bir veri toplama dahi katıldığınız sunucunun farklı bir politikası olabilir. \n\n %s politikası sizi tatmin etmiyorsa geri giderek farklı bir sunucu seçebilirsiniz. Kabul ediyorum Bu liste boş @@ -392,9 +392,9 @@ Yer İmi Kaldır Yer İmleri Favorilerin - Hoşgeldin + Hoşgeldiniz Hesabınızı oluşturduğunuz sunucu ile giriş yapın. - Sunucu URL\'si + Sunucu bağlantısı Herhangi bir seçim yapmadan devam ederseniz dilinize göre bir sunucu seçeceğiz. Herhangi Bir Dil Koşulsuz Kayıt @@ -419,7 +419,7 @@ Reddet Kısacası: Hiçbir veri işlemiyor ya da toplamıyoruz. - %s\'yi reddet + %s \'yi reddet Hakkımda Hesaplar takip ediliyor… @@ -437,12 +437,12 @@ Onaylanmış bağlantı Göster Gizle - %s\'e katıl + %s katıl Başka sunucu seç veya - Daha fazla bilgi edinin + Daha fazlası Mastodon\'a hoş geldiniz - Mastodon merkezi olmayan bir sosyal ağdır, yani tek bir şirket tarafından kontrol edilmemektedir. Hepsi birbirine bağlı, bağımsız olarak işletilen birçok sunucudan oluşur. + Mastodon , Merkezi olmayan bir sosyal ağdır, yani tek bir şirket tarafından kontrol edilmemektedir. Hepsi birbirine bağlı, bağımsız olarak işletilen birçok sunucudan oluşur. Sunucular nelerdir? - + From 02b1ad8d7ab065c93aabad99ce70b754d95191ea Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Jun 2023 00:56:52 +0200 Subject: [PATCH 51/79] New translations strings.xml (Swedish) --- mastodon/src/main/res/values-sv-rSE/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/res/values-sv-rSE/strings.xml b/mastodon/src/main/res/values-sv-rSE/strings.xml index e4209c1ab..087926cf9 100644 --- a/mastodon/src/main/res/values-sv-rSE/strings.xml +++ b/mastodon/src/main/res/values-sv-rSE/strings.xml @@ -422,6 +422,7 @@ Konton Verifierad länk Visa + Gå med %s eller Välkommen till Mastodon Vad är servrar? From 24e5bda8d3c9c13572fc9b158bd15b0a4c4ef6bb Mon Sep 17 00:00:00 2001 From: sk Date: Tue, 6 Jun 2023 10:30:32 +0200 Subject: [PATCH 52/79] fix profile options icons --- .../org/joinmastodon/android/fragments/ProfileFragment.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index d8ccc4c30..d3c70d1d2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -596,6 +596,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList return false; } + @Override + protected boolean wantsToolbarMenuIconsTinted() { + return false; + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ if(isOwnProfile && isInEditMode){ From 659b4e2fcd339b586deb9ac3e44840a80ee015b3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Jun 2023 11:55:59 +0200 Subject: [PATCH 53/79] New translations strings.xml (Swedish) --- mastodon/src/main/res/values-sv-rSE/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mastodon/src/main/res/values-sv-rSE/strings.xml b/mastodon/src/main/res/values-sv-rSE/strings.xml index 087926cf9..614e5f0b0 100644 --- a/mastodon/src/main/res/values-sv-rSE/strings.xml +++ b/mastodon/src/main/res/values-sv-rSE/strings.xml @@ -416,12 +416,14 @@ %1$s tillåter inte registrering från %2$s. Prova en annan eller <a>välj en annan server</a>. Det här användarnamnet är redan taget. Visa ändå + Välj en eller flera Spara ändringar Tidslinje Visa alla Konton Verifierad länk Visa + Dölj Gå med %s eller Välkommen till Mastodon From c4ac4ee17379354c932e6a6ad420efb32570ac69 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 6 Jun 2023 13:23:46 +0200 Subject: [PATCH 54/79] New translations strings.xml (Swedish) --- mastodon/src/main/res/values-sv-rSE/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/res/values-sv-rSE/strings.xml b/mastodon/src/main/res/values-sv-rSE/strings.xml index 614e5f0b0..0cef3870d 100644 --- a/mastodon/src/main/res/values-sv-rSE/strings.xml +++ b/mastodon/src/main/res/values-sv-rSE/strings.xml @@ -426,6 +426,7 @@ Dölj Gå med %s eller + Läs mer Välkommen till Mastodon Vad är servrar? From 68921d0f0b1b1a87108699ec8106eb2ff141ffa8 Mon Sep 17 00:00:00 2001 From: sk Date: Tue, 6 Jun 2023 14:23:24 +0200 Subject: [PATCH 55/79] fix footer being too close to next header --- .../android/ui/displayitems/FooterStatusDisplayItem.java | 2 +- .../android/ui/displayitems/TextStatusDisplayItem.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java index 93facae14..52c4d3726 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java @@ -144,7 +144,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams(); params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, - condenseBottom ? V.dp(-8) : 0); + condenseBottom ? V.dp(-5) : 0); itemView.requestLayout(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java index 8154af905..33ded1cff 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java @@ -241,7 +241,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{ boolean nextIsFooter = item.parentFragment.getDisplayItems().size() > nextPos && item.parentFragment.getDisplayItems().get(nextPos) instanceof FooterStatusDisplayItem; int bottomPadding = (translateVisible && nextIsFooter) ? 0 - : nextIsFooter ? V.dp(8) + : nextIsFooter ? V.dp(6) : V.dp(12); itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), bottomPadding); From 969f29e2e96c6dfef570bc9de924da30e463b986 Mon Sep 17 00:00:00 2001 From: sk Date: Tue, 6 Jun 2023 16:55:20 +0200 Subject: [PATCH 56/79] support string res for small text item --- .../joinmastodon/android/fragments/SettingsFragment.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index a36b18a5d..b495b1316 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -835,7 +835,11 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide } private class SmallTextItem extends Item { - private String text; + private final String text; + + public SmallTextItem(@StringRes int text) { + this.text = getString(text); + } public SmallTextItem(String text) { this.text = text; From 4258c55b88d195ac50223c5baf14091340129c9c Mon Sep 17 00:00:00 2001 From: sk Date: Tue, 6 Jun 2023 17:04:29 +0200 Subject: [PATCH 57/79] implement fetching listings from remote instances --- .../android/GlobalUserPreferences.java | 3 + .../android/api/MastodonAPIRequest.java | 28 +++- .../requests/accounts/GetAccountByHandle.java | 15 ++ .../api/session/AccountSessionManager.java | 5 + .../android/fragments/ProfileFragment.java | 91 +++++++---- .../android/fragments/SettingsFragment.java | 5 + .../AccountRelatedAccountListFragment.java | 45 +++++- .../account_list/BaseAccountListFragment.java | 15 +- .../account_list/FollowerListFragment.java | 4 +- .../account_list/FollowingListFragment.java | 4 +- .../PaginatedAccountListFragment.java | 150 +++++++++++++++++- .../StatusFavoritesListFragment.java | 8 +- .../StatusReblogsListFragment.java | 8 +- .../StatusRelatedAccountListFragment.java | 50 +++++- .../joinmastodon/android/model/Account.java | 14 ++ .../joinmastodon/android/model/BaseModel.java | 8 + .../android/ui/utils/UiUtils.java | 15 +- .../ic_fluent_communication_24_regular.xml | 3 + mastodon/src/main/res/values/strings_sk.xml | 4 + 19 files changed, 422 insertions(+), 53 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountByHandle.java create mode 100644 mastodon/src/main/res/drawable/ic_fluent_communication_24_regular.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java index d789d321d..3366f213f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java +++ b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java @@ -48,6 +48,7 @@ public class GlobalUserPreferences{ public static boolean replyLineAboveHeader; public static boolean compactReblogReplyLine; public static boolean confirmBeforeReblog; + public static boolean allowRemoteLoading; public static String publishButtonText; public static ThemePreference theme; public static ColorPreference color; @@ -127,6 +128,7 @@ public class GlobalUserPreferences{ replyVisibility=prefs.getString("replyVisibility", null); accountsWithContentTypesEnabled=prefs.getStringSet("accountsWithContentTypesEnabled", new HashSet<>()); accountsDefaultContentTypes=fromJson(prefs.getString("accountsDefaultContentTypes", null), accountsDefaultContentTypesType, new HashMap<>()); + allowRemoteLoading=prefs.getBoolean("allowRemoteLoading", true); try { color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name())); @@ -176,6 +178,7 @@ public class GlobalUserPreferences{ .putString("replyVisibility", replyVisibility) .putStringSet("accountsWithContentTypesEnabled", accountsWithContentTypesEnabled) .putString("accountsDefaultContentTypes", gson.toJson(accountsDefaultContentTypes)) + .putBoolean("allowRemoteLoading", allowRemoteLoading) .apply(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java index 44a740401..f9dd1ee72 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java @@ -20,9 +20,11 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import androidx.annotation.CallSuper; +import androidx.annotation.Nullable; import androidx.annotation.StringRes; import me.grishka.appkit.api.APIRequest; import me.grishka.appkit.api.Callback; @@ -44,7 +46,7 @@ public abstract class MastodonAPIRequest extends APIRequest{ TypeToken respTypeToken; Call okhttpCall; Token token; - boolean canceled; + boolean canceled, isRemote; Map headers; private ProgressDialog progressDialog; protected boolean removeUnsupportedItems; @@ -101,6 +103,21 @@ public abstract class MastodonAPIRequest extends APIRequest{ return this; } + public MastodonAPIRequest execRemote(String domain) { + return execRemote(domain, null); + } + + public MastodonAPIRequest execRemote(String domain, @Nullable AccountSession remoteSession) { + this.isRemote = true; + return Optional.ofNullable(remoteSession) + .or(() -> AccountSessionManager.getInstance().getLoggedInAccounts().stream() + .filter(acc -> acc.domain.equals(domain)) + .findAny()) + .map(AccountSession::getID) + .map(this::exec) + .orElse(this.execNoAuth(domain)); + } + public MastodonAPIRequest wrapProgress(Activity activity, @StringRes int message, boolean cancelable){ return wrapProgress(activity, message, cancelable, null); } @@ -167,6 +184,7 @@ public abstract class MastodonAPIRequest extends APIRequest{ @CallSuper public void validateAndPostprocessResponse(T respObj, Response httpResponse) throws IOException{ if(respObj instanceof BaseModel){ + ((BaseModel) respObj).isRemote = isRemote; ((BaseModel) respObj).postprocess(); }else if(respObj instanceof List){ if(removeUnsupportedItems){ @@ -175,6 +193,7 @@ public abstract class MastodonAPIRequest extends APIRequest{ Object item=itr.next(); if(item instanceof BaseModel){ try{ + ((BaseModel) item).isRemote = isRemote; ((BaseModel) item).postprocess(); }catch(ObjectValidationException x){ Log.w(TAG, "Removing invalid object from list", x); @@ -182,15 +201,20 @@ public abstract class MastodonAPIRequest extends APIRequest{ } } } + // no idea why we're post-processing twice, but well, as long + // as upstream does it like this, i don't wanna break anything for(Object item:((List) respObj)){ if(item instanceof BaseModel){ + ((BaseModel) item).isRemote = isRemote; ((BaseModel) item).postprocess(); } } }else{ for(Object item:((List) respObj)){ - if(item instanceof BaseModel) + if(item instanceof BaseModel) { + ((BaseModel) item).isRemote = isRemote; ((BaseModel) item).postprocess(); + } } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountByHandle.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountByHandle.java new file mode 100644 index 000000000..011a8bf91 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/accounts/GetAccountByHandle.java @@ -0,0 +1,15 @@ +package org.joinmastodon.android.api.requests.accounts; + +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.model.Account; + +public class GetAccountByHandle extends MastodonAPIRequest{ + /** + * note that this method usually only returns a result if the instance already knows about an + * account - so it makes sense for looking up local users, search might be preferred otherwise + */ + public GetAccountByHandle(String acct){ + super(HttpMethod.GET, "/accounts/lookup", Account.class); + addQueryParameter("acct", acct); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java index 3b68ac36e..b2631b39c 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/session/AccountSessionManager.java @@ -160,6 +160,11 @@ public class AccountSessionManager{ return sessions.get(id); } + @Nullable + public AccountSession tryGetAccount(Account account) { + return sessions.get(account.getDomainFromURL() + "_" + account.id); + } + @Nullable public AccountSession getLastActiveAccount(){ if(sessions.isEmpty() || lastActiveAccountID==null) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index d3c70d1d2..9f1c95296 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -45,6 +45,7 @@ import android.widget.Toolbar; import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.MastodonErrorResponse; import org.joinmastodon.android.api.requests.accounts.GetAccountByID; import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships; import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses; @@ -137,7 +138,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList private TextView followsYouView; private ViewGroup rolesView; - private Account account; + private Account account, remoteAccount; private String accountID; private String domain; private Relationship relationship; @@ -176,7 +177,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList accountID=getArguments().getString("account"); domain=AccountSessionManager.getInstance().getAccount(accountID).domain; - if(getArguments().containsKey("profileAccount")){ + if (getArguments().containsKey("remoteAccount")) { + remoteAccount = Parcels.unwrap(getArguments().getParcelable("remoteAccount")); + if(!getArguments().getBoolean("noAutoLoad", false)) + loadData(); + } else if(getArguments().containsKey("profileAccount")){ account=Parcels.unwrap(getArguments().getParcelable("profileAccount")); profileAccountID=account.id; isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account); @@ -347,36 +352,55 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList return sizeWrapper; } + private void onAccountLoaded(Account result) { + account=result; + isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account); + bindHeaderView(); + dataLoaded(); + if(!tabLayoutMediator.isAttached()) + tabLayoutMediator.attach(); + if(!isOwnProfile) + loadRelationship(); + else + AccountSessionManager.getInstance().updateAccountInfo(accountID, account); + if(refreshing){ + refreshing=false; + refreshLayout.setRefreshing(false); + if(postsFragment.loaded) + postsFragment.onRefresh(); + if(postsWithRepliesFragment.loaded) + postsWithRepliesFragment.onRefresh(); + if(pinnedPostsFragment.loaded) + pinnedPostsFragment.onRefresh(); + if(mediaFragment.loaded) + mediaFragment.onRefresh(); + } + V.setVisibilityAnimated(fab, View.VISIBLE); + } + @Override protected void doLoadData(){ + if (remoteAccount != null) { + UiUtils.lookupAccountHandle(getContext(), accountID, remoteAccount.getFullyQualifiedName(), (c, args) -> { + if (getContext() == null) return; + if (args == null || !args.containsKey("profileAccount")) { + onError(new MastodonErrorResponse( + getContext().getString(R.string.sk_error_loading_profile), + 0, null + )); + return; + } + onAccountLoaded(Parcels.unwrap(args.getParcelable("profileAccount"))); + }); + return; + } + currentRequest=new GetAccountByID(profileAccountID) .setCallback(new SimpleCallback<>(this){ @Override public void onSuccess(Account result){ if (getActivity() == null) return; - account=result; - isOwnProfile=AccountSessionManager.getInstance().isSelf(accountID, account); - bindHeaderView(); - dataLoaded(); - if(!tabLayoutMediator.isAttached()) - tabLayoutMediator.attach(); - if(!isOwnProfile) - loadRelationship(); - else - AccountSessionManager.getInstance().updateAccountInfo(accountID, account); - if(refreshing){ - refreshing=false; - refreshLayout.setRefreshing(false); - if(postsFragment.loaded) - postsFragment.onRefresh(); - if(postsWithRepliesFragment.loaded) - postsWithRepliesFragment.onRefresh(); - if(pinnedPostsFragment.loaded) - pinnedPostsFragment.onRefresh(); - if(mediaFragment.loaded) - mediaFragment.onRefresh(); - } - V.setVisibilityAnimated(fab, View.VISIBLE); + onAccountLoaded(result); } }) .exec(accountID); @@ -490,9 +514,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(100), V.dp(100))); ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000)); SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName); - HtmlParser.parseCustomEmoji(ssb, account.emojis); - name.setText(ssb); - setTitle(ssb); + HtmlParser.parseCustomEmoji(ssb, account.emojis); + name.setText(ssb); + setTitle(ssb); if (account.roles != null && !account.roles.isEmpty()) { rolesView.setVisibility(View.VISIBLE); @@ -511,13 +535,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account); + String acct = ((isSelf || account.isRemote) + ? account.getFullyQualifiedName() + : account.acct); if(account.locked){ ssb=new SpannableStringBuilder("@"); - ssb.append(account.acct); - if(isSelf){ - ssb.append('@'); - ssb.append(domain); - } + ssb.append(acct); ssb.append(" "); Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate(); lock.setBounds(0, 0, lock.getIntrinsicWidth(), lock.getIntrinsicHeight()); @@ -526,7 +549,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList username.setText(ssb); }else{ // noinspection SetTextI18n - username.setText('@'+account.acct+(isSelf ? ('@'+domain) : "")); + username.setText('@'+acct); } CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID); if(TextUtils.isEmpty(parsedBio)){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index b495b1316..fe1d8edea 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -219,6 +219,11 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide GlobalUserPreferences.confirmBeforeReblog=i.checked; GlobalUserPreferences.save(); })); + items.add(new SwitchItem(R.string.sk_settings_allow_remote_loading, R.drawable.ic_fluent_communication_24_regular, GlobalUserPreferences.allowRemoteLoading, i->{ + GlobalUserPreferences.allowRemoteLoading=i.checked; + GlobalUserPreferences.save(); + })); + items.add(new SmallTextItem(R.string.sk_settings_allow_remote_loading_explanation)); items.add(new HeaderItem(R.string.sk_timelines)); items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java index 3a27bca56..43ad6ffa4 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/AccountRelatedAccountListFragment.java @@ -3,16 +3,27 @@ package org.joinmastodon.android.fragments.account_list; import android.net.Uri; import android.os.Bundle; +import org.joinmastodon.android.R; +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.api.requests.accounts.GetAccountByHandle; +import org.joinmastodon.android.api.session.AccountSession; +import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.model.Account; import org.parceler.Parcels; -public abstract class AccountRelatedAccountListFragment extends PaginatedAccountListFragment{ +import java.util.Optional; + +public abstract class AccountRelatedAccountListFragment extends PaginatedAccountListFragment { protected Account account; + protected String initialSubtitle = ""; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); account=Parcels.unwrap(getArguments().getParcelable("targetAccount")); + if (getArguments().containsKey("remoteAccount")) { + remoteInfo = Parcels.unwrap(getArguments().getParcelable("remoteAccount")); + } setTitle("@"+account.acct); } @@ -22,4 +33,36 @@ public abstract class AccountRelatedAccountListFragment extends PaginatedAccount ? "/users/" + account.id : '@' + account.acct).build(); } + + @Override + public String getRemoteDomain() { + return account.getDomainFromURL(); + } + + @Override + public Account getCurrentInfo() { + return doneWithHomeInstance && remoteInfo != null ? remoteInfo : account; + } + + @Override + protected MastodonAPIRequest loadRemoteInfo() { + return new GetAccountByHandle(account.acct); + } + + @Override + protected AccountSession getRemoteSession() { + return Optional.ofNullable(remoteInfo) + .map(AccountSessionManager.getInstance()::tryGetAccount) + .orElse(null); + } + + @Override + protected void onRemoteLoadingFailed() { + super.onRemoteLoadingFailed(); + String prefix = initialSubtitle == null ? "" : + initialSubtitle + " " + getContext().getString(R.string.sk_separator) + " "; + String str = prefix + + getContext().getString(R.string.sk_no_remote_info_hint, getSession().domain); + setSubtitle(str); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java index 312976597..bcf70060f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java @@ -1,5 +1,6 @@ package org.joinmastodon.android.fragments.account_list; +import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.app.assist.AssistContent; import android.content.Intent; @@ -47,6 +48,7 @@ import java.util.stream.Collectors; import androidx.annotation.CallSuper; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; import me.grishka.appkit.Nav; import me.grishka.appkit.api.APIRequest; @@ -243,10 +245,13 @@ public abstract class BaseAccountListFragment extends RecyclerFragment onCreateRequest(String maxID, int count){ - return new GetAccountFollowers(account.id, maxID, count); + return new GetAccountFollowers(getCurrentInfo().id, maxID, count); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java index a9b73e921..0e1c2bacb 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/FollowingListFragment.java @@ -13,12 +13,12 @@ public class FollowingListFragment extends AccountRelatedAccountListFragment{ @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); - setSubtitle(getResources().getQuantityString(R.plurals.x_following, (int)(account.followingCount%1000), account.followingCount)); + setSubtitle(initialSubtitle = getResources().getQuantityString(R.plurals.x_following, (int)(account.followingCount%1000), account.followingCount)); } @Override public HeaderPaginationRequest onCreateRequest(String maxID, int count){ - return new GetAccountFollowing(account.id, maxID, count); + return new GetAccountFollowing(getCurrentInfo().id, maxID, count); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java index 5b34019f1..f6e77efe8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/PaginatedAccountListFragment.java @@ -1,33 +1,173 @@ package org.joinmastodon.android.fragments.account_list; +import android.os.Bundle; +import android.view.View; + +import org.joinmastodon.android.GlobalUserPreferences; +import org.joinmastodon.android.api.MastodonAPIRequest; import org.joinmastodon.android.api.requests.HeaderPaginationRequest; +import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.HeaderPaginationList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.stream.Collectors; +import me.grishka.appkit.api.Callback; +import me.grishka.appkit.api.ErrorResponse; import me.grishka.appkit.api.SimpleCallback; -public abstract class PaginatedAccountListFragment extends BaseAccountListFragment{ +public abstract class PaginatedAccountListFragment extends BaseAccountListFragment{ private String nextMaxID; + private MastodonAPIRequest remoteInfoRequest; + protected boolean doneWithHomeInstance, remoteRequestFailed, startedRemoteLoading, remoteDisabled; + protected int localOffset; + protected T remoteInfo; public abstract HeaderPaginationRequest onCreateRequest(String maxID, int count); + protected abstract MastodonAPIRequest loadRemoteInfo(); + public abstract T getCurrentInfo(); + public abstract String getRemoteDomain(); + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + // already have remote info (e.g. from arguments), so no need to fetch it again + if (remoteInfo != null) { + onRemoteInfoLoaded(remoteInfo); + return; + } + + remoteDisabled = !GlobalUserPreferences.allowRemoteLoading + || getSession().domain.equals(getRemoteDomain()); + if (!remoteDisabled) { + remoteInfoRequest = loadRemoteInfo().setCallback(new Callback<>() { + @Override + public void onSuccess(T result) { + if (getContext() == null) return; + onRemoteInfoLoaded(result); + } + + @Override + public void onError(ErrorResponse error) { + if (getContext() == null) return; + onRemoteLoadingFailed(); + } + }); + remoteInfoRequest.execRemote(getRemoteDomain(), getRemoteSession()); + } + } + + /** + * override to provide an ideal account session (e.g. if you're logged into the author's remote + * account) to make the remote request from. if null is provided, will try to get any session + * on the remote domain, or tries the request without authentication. + */ + protected AccountSession getRemoteSession() { + return null; + } + + protected void onRemoteInfoLoaded(T info) { + this.remoteInfo = info; + this.remoteInfoRequest = null; + maybeStartLoadingRemote(); + } + + protected void onRemoteLoadingFailed() { + this.remoteRequestFailed = true; + this.remoteInfo = null; + this.remoteInfoRequest = null; + if (doneWithHomeInstance) dataLoaded(); + } + + @Override + public void dataLoaded() { + super.dataLoaded(); + footerProgress.setVisibility(View.GONE); + } + + private void maybeStartLoadingRemote() { + if (startedRemoteLoading || remoteDisabled) return; + if (!remoteRequestFailed) { + if (data.size() == 0) showProgress(); + else footerProgress.setVisibility(View.VISIBLE); + } + if (doneWithHomeInstance && remoteInfo != null) { + startedRemoteLoading = true; + loadData(localOffset, itemsPerPage * 2); + } + } + + @Override + public void onRefresh() { + localOffset = 0; + doneWithHomeInstance = false; + startedRemoteLoading = false; + super.onRefresh(); + } + + @Override + public void loadData(int offset, int count) { + // always subtract the amount loaded through the home instance once loading from remote + // since loadData gets called with data.size() (data includes both local and remote) + if (doneWithHomeInstance) offset -= localOffset; + super.loadData(offset, count); + } + @Override protected void doLoadData(int offset, int count){ - currentRequest=onCreateRequest(offset==0 ? null : nextMaxID, count) + MastodonAPIRequest request = onCreateRequest(offset==0 ? null : nextMaxID, count) .setCallback(new SimpleCallback<>(this){ @Override public void onSuccess(HeaderPaginationList result){ + boolean justRefreshed = !doneWithHomeInstance && offset == 0; + Collection d = justRefreshed ? List.of() : data; + if(result.nextPageUri!=null) nextMaxID=result.nextPageUri.getQueryParameter("max_id"); else nextMaxID=null; if (getActivity() == null) return; - onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null); + List items = result.stream() + .filter(a -> d.size() > 1000 || d.stream() + .noneMatch(i -> i.account.url.equals(a.url))) + .map(AccountItem::new) + .collect(Collectors.toList()); + + boolean hasMore = nextMaxID != null; + + if (!hasMore && !doneWithHomeInstance) { + // only runs last time data was fetched from the home instance + localOffset = d.size() + items.size(); + doneWithHomeInstance = true; + } + + onDataLoaded(items, hasMore); + if (doneWithHomeInstance) maybeStartLoadingRemote(); } - }) - .exec(accountID); + + @Override + public void onError(ErrorResponse error) { + if (doneWithHomeInstance) { + onRemoteLoadingFailed(); + onDataLoaded(Collections.emptyList(), false); + return; + } + super.onError(error); + } + }); + + if (doneWithHomeInstance && remoteInfo == null) return; // we are waiting + if (doneWithHomeInstance && remoteInfo != null) { + request.execRemote(getRemoteDomain(), getRemoteSession()); + } else { + request.exec(accountID); + } + currentRequest = request; } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java index 915a8766e..20d0c2acf 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusFavoritesListFragment.java @@ -7,17 +7,23 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.HeaderPaginationRequest; import org.joinmastodon.android.api.requests.statuses.GetStatusFavorites; import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Status; public class StatusFavoritesListFragment extends StatusRelatedAccountListFragment{ @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); + updateTitle(status); + } + + @Override + protected void updateTitle(Status status) { setTitle(getResources().getQuantityString(R.plurals.x_favorites, (int)(status.favouritesCount%1000), status.favouritesCount)); } @Override public HeaderPaginationRequest onCreateRequest(String maxID, int count){ - return new GetStatusFavorites(status.id, maxID, count); + return new GetStatusFavorites(getCurrentInfo().id, maxID, count); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java index 1308a14a9..3c5a5e228 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusReblogsListFragment.java @@ -7,17 +7,23 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.HeaderPaginationRequest; import org.joinmastodon.android.api.requests.statuses.GetStatusReblogs; import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Status; public class StatusReblogsListFragment extends StatusRelatedAccountListFragment{ @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); + updateTitle(status); + } + + @Override + protected void updateTitle(Status status) { setTitle(getResources().getQuantityString(R.plurals.x_reblogs, (int)(status.reblogsCount%1000), status.reblogsCount)); } @Override public HeaderPaginationRequest onCreateRequest(String maxID, int count){ - return new GetStatusReblogs(status.id, maxID, count); + return new GetStatusReblogs(getCurrentInfo().id, maxID, count); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java index d0499afa6..aeee1fdc9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/StatusRelatedAccountListFragment.java @@ -3,12 +3,27 @@ package org.joinmastodon.android.fragments.account_list; import android.net.Uri; import android.os.Bundle; +import org.joinmastodon.android.R; +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.api.requests.statuses.GetStatusByID; +import org.joinmastodon.android.api.session.AccountSession; +import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.model.Status; import org.parceler.Parcels; -public abstract class StatusRelatedAccountListFragment extends PaginatedAccountListFragment{ +import java.util.Optional; + +public abstract class StatusRelatedAccountListFragment extends PaginatedAccountListFragment { protected Status status; + protected abstract void updateTitle(Status status); + + protected MastodonAPIRequest loadRemoteInfo() { + String[] parts = status.url.split("/"); + if (parts.length == 0) return null; + return new GetStatusByID(parts[parts.length - 1]); + } + @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); @@ -17,7 +32,7 @@ public abstract class StatusRelatedAccountListFragment extends PaginatedAccountL @Override protected boolean hasSubtitle(){ - return false; + return remoteRequestFailed; } @Override @@ -28,4 +43,35 @@ public abstract class StatusRelatedAccountListFragment extends PaginatedAccountL : '@' + status.account.acct + '/' + status.id) .build(); } + + @Override + public String getRemoteDomain() { + return Uri.parse(status.url).getHost(); + } + + @Override + public Status getCurrentInfo() { + return doneWithHomeInstance && remoteInfo != null ? remoteInfo : status; + } + + @Override + protected AccountSession getRemoteSession() { + return Optional.ofNullable(remoteInfo) + .map(s -> s.account) + .map(AccountSessionManager.getInstance()::tryGetAccount) + .orElse(null); + } + + @Override + protected void onRemoteInfoLoaded(Status info) { + super.onRemoteInfoLoaded(info); + updateTitle(remoteInfo); + } + + @Override + protected void onRemoteLoadingFailed() { + super.onRemoteLoadingFailed(); + setSubtitle(getContext().getString(R.string.sk_no_remote_info_hint, getSession().domain)); + updateToolbar(); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Account.java b/mastodon/src/main/java/org/joinmastodon/android/model/Account.java index b4424e0a6..1fdfe08cc 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Account.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Account.java @@ -1,7 +1,10 @@ package org.joinmastodon.android.model; +import android.net.Uri; import android.text.TextUtils; +import androidx.annotation.Nullable; + import org.joinmastodon.android.api.ObjectValidationException; import org.joinmastodon.android.api.RequiredField; import org.parceler.Parcel; @@ -135,6 +138,8 @@ public class Account extends BaseModel implements Searchable{ public List roles; + public @Nullable String fqn; // akkoma has this, mastodon't + @Override public String getQuery() { return url; @@ -162,6 +167,7 @@ public class Account extends BaseModel implements Searchable{ moved.postprocess(); if(TextUtils.isEmpty(displayName)) displayName=username; + if(fqn == null) fqn = getFullyQualifiedName(); } public boolean isLocal(){ @@ -173,6 +179,10 @@ public class Account extends BaseModel implements Searchable{ return parts.length==1 ? null : parts[1]; } + public String getDomainFromURL() { + return Uri.parse(url).getHost(); + } + public String getDisplayUsername(){ return '@'+acct; } @@ -181,6 +191,10 @@ public class Account extends BaseModel implements Searchable{ return '@'+acct.split("@")[0]; } + public String getFullyQualifiedName() { + return fqn != null ? fqn : acct.split("@")[0] + "@" + getDomainFromURL(); + } + @Override public String toString(){ return "Account{"+ diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java b/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java index b2ec8898f..051be6240 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/BaseModel.java @@ -11,6 +11,14 @@ import androidx.annotation.CallSuper; import androidx.annotation.NonNull; public abstract class BaseModel implements Cloneable{ + + /** + * indicates the profile has been fetched from a foreign instance. + * + * @see MastodonAPIRequest#execRemote + */ + public transient boolean isRemote; + @CallSuper public void postprocess() throws ObjectValidationException{ try{ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 0c69a5a7d..82913f383 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -56,6 +56,7 @@ import org.joinmastodon.android.E; import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.MastodonApp; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.MastodonErrorResponse; import org.joinmastodon.android.api.StatusInteractionController; import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked; import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed; @@ -1136,6 +1137,12 @@ public class UiUtils { } } + public static void lookupAccountHandle(Context context, String accountID, String query, BiConsumer, Bundle> go) { + parseFediverseHandle(query).ifPresentOrElse( + handle -> lookupAccountHandle(context, accountID, handle, go), + () -> go.accept(null, null) + ); + } public static void lookupAccountHandle(Context context, String accountID, Pair> queryHandle, BiConsumer, Bundle> go) { String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse("")); new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true) @@ -1153,12 +1160,18 @@ public class UiUtils { return; } Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show(); + args.putString("error", context.getString(R.string.sk_resource_not_found)); go.accept(null, null); } @Override public void onError(ErrorResponse error) { - + Bundle args = new Bundle(); + if (error instanceof MastodonErrorResponse e) { + args.putString("error", e.error); + args.putInt("httpStatus", e.httpStatus); + } + go.accept(null, args); } }).exec(accountID); } diff --git a/mastodon/src/main/res/drawable/ic_fluent_communication_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_communication_24_regular.xml new file mode 100644 index 000000000..a9f610543 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_communication_24_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index 9dc3bbbd3..221baaac9 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -293,4 +293,8 @@ Could not open in app Share with account Share or open with account + remote info unavailable + Failed loading the profile on your home instance. + Load info from remote instances + Try fetching more accurate listings for followers, likes and boosts by loading the information from the instance of origin. \ No newline at end of file From 6ac5d957fe8283ca6d8e81bd3284c122d6d7f700 Mon Sep 17 00:00:00 2001 From: Choukajohn Date: Mon, 5 Jun 2023 19:24:30 +0000 Subject: [PATCH 58/79] Translated using Weblate (French) Currently translated at 100.0% (293 of 293 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/ --- mastodon/src/main/res/values-fr-rFR/strings_sk.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/res/values-fr-rFR/strings_sk.xml b/mastodon/src/main/res/values-fr-rFR/strings_sk.xml index 01e013d7a..5f8a8baad 100644 --- a/mastodon/src/main/res/values-fr-rFR/strings_sk.xml +++ b/mastodon/src/main/res/values-fr-rFR/strings_sk.xml @@ -290,7 +290,8 @@ Ouvrir dans l\'application Partager avec le compte Partager ou ouvrir avec le compte - Ce sont les publications les plus récentes des personnes présentes dans la bulle de votre serveur Akkoma. + Ce sont les messages les plus récents du réseau organisés par vos administrateurs d\'instance. Bulle Informations sur l\'instance temporairement indisponibles + Impossible de l\'ouvrir dans l\'application \ No newline at end of file From 9f65b8112a63be6e29f677796d5330868c379bb9 Mon Sep 17 00:00:00 2001 From: sk22 Date: Tue, 6 Jun 2023 15:07:28 +0000 Subject: [PATCH 59/79] Translated using Weblate (German) Currently translated at 100.0% (297 of 297 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/ --- mastodon/src/main/res/values-de-rDE/strings_sk.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mastodon/src/main/res/values-de-rDE/strings_sk.xml b/mastodon/src/main/res/values-de-rDE/strings_sk.xml index 8f57366d8..4e972f161 100644 --- a/mastodon/src/main/res/values-de-rDE/strings_sk.xml +++ b/mastodon/src/main/res/values-de-rDE/strings_sk.xml @@ -292,4 +292,8 @@ Informationen zur Instanz momentan nicht verfügbar In App öffnen Dadurch lässt beim Erstellen von Beiträgen ein Inhaltstyp wie Markdown angeben. Nicht alle Instanzen unterstützen das. + Infos von Remote-Instanzen laden + keine Remote-Infos abrufbar + Konnte das Profil auf deiner Heim-Instanz nicht laden. + Für vollständigere Auflistung von Follower*innen, Likes und Boosts können die Informationen von der Ursprungs-Instanz geladen werden. \ No newline at end of file From ad13b1e9277027fda0f8702893e8c67aa1638f66 Mon Sep 17 00:00:00 2001 From: sk Date: Tue, 6 Jun 2023 17:13:30 +0200 Subject: [PATCH 60/79] bump version --- mastodon/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 6076b27c6..d43bc070a 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -15,8 +15,8 @@ android { applicationId "org.joinmastodon.android.sk" minSdk 23 targetSdk 33 - versionCode 90 - versionName "1.2.3+fork.90" + versionCode 91 + versionName "1.2.3+fork.91" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW'] } From 0091ae87ced6a4430fdfc586b45e13886030fccf Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 15:17:44 +0200 Subject: [PATCH 61/79] fix issues with external share closes sk22#550 --- .../android/ExternalShareActivity.java | 16 ++++++++++--- .../joinmastodon/android/MainActivity.java | 4 ++-- .../android/ui/AccountSwitcherSheet.java | 3 +-- .../android/ui/utils/UiUtils.java | 23 ++++++++----------- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java index 972e7504d..40418d5fe 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java @@ -9,6 +9,7 @@ import android.text.TextUtils; import android.util.Pair; import android.widget.Toast; +import org.joinmastodon.android.api.MastodonAPIRequest; import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.fragments.ComposeFragment; @@ -43,7 +44,7 @@ public class ExternalShareActivity extends FragmentStackActivity{ finish(); } else if (isOpenable || sessions.size() > 1) { AccountSwitcherSheet sheet = new AccountSwitcherSheet(this, null, true, isOpenable); - if (isOpenable) sheet.setOnClick((accountId, open) -> { + sheet.setOnClick((accountId, open) -> { if (open && text.isPresent()) { BiConsumer, Bundle> callback = (clazz, args) -> { if (clazz == null) { @@ -59,8 +60,17 @@ public class ExternalShareActivity extends FragmentStackActivity{ finish(); startActivity(intent); }; - if (isFediUrl) UiUtils.lookupURL(this, accountId, text.get(), false, callback); - else UiUtils.lookupAccountHandle(this, accountId, fediHandle.get(), callback); + + fediHandle + .>map(handle -> + UiUtils.lookupAccountHandle(this, accountId, handle, callback)) + .or(() -> Optional.ofNullable( + UiUtils.lookupURL(this, accountId, text.get(), false, callback))) + .ifPresent(req -> + req.wrapProgress(this, R.string.loading, true, d -> { + UiUtils.transformDialogForLookup(this, accountId, isFediUrl ? text.get() : null, d); + d.setOnDismissListener((ev) -> finish()); + })); } else { openComposeFragment(accountId); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java index 1f19516b8..67b29e14f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java @@ -127,8 +127,8 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis } private void showFragmentForExternalShare(Bundle args) { - String clazz = args.getString("fromExternalShare"); - Fragment fragment = switch (clazz) { + String className = args.getString("fromExternalShare"); + Fragment fragment = switch (className) { case "ThreadFragment" -> new ThreadFragment(); case "ProfileFragment" -> new ProfileFragment(); default -> null; diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java index e6cc268ab..70db77ab7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/AccountSwitcherSheet.java @@ -75,8 +75,7 @@ public class AccountSwitcherSheet extends BottomSheet{ this.fragment=fragment; this.externalShare = externalShare; this.openInApp = openInApp; - this.onClick = onClick; - + accounts=AccountSessionManager.getInstance().getLoggedInAccounts().stream().map(WrappedAccount::new).collect(Collectors.toList()); list=new UsableRecyclerView(activity); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 82913f383..dc538f3a8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -56,6 +56,7 @@ import org.joinmastodon.android.E; import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.MastodonApp; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.MastodonAPIRequest; import org.joinmastodon.android.api.MastodonErrorResponse; import org.joinmastodon.android.api.StatusInteractionController; import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked; @@ -1095,7 +1096,7 @@ public class UiUtils { openURL(context, accountID, url, true); } - private static void transformDialogForLookup(Context context, String accountID, @Nullable String url, ProgressDialog dialog) { + public static void transformDialogForLookup(Context context, String accountID, @Nullable String url, ProgressDialog dialog) { if (accountID != null) { dialog.setTitle(context.getString(R.string.sk_loading_resource_on_instance_title, getInstanceName(accountID))); } else { @@ -1114,7 +1115,8 @@ public class UiUtils { lookupURL(context, accountID, url, launchBrowser, (clazz, args) -> { if (clazz == null) return; Nav.go((Activity) context, clazz, args); - }); + }).wrapProgress((Activity) context, R.string.loading, true, d -> + transformDialogForLookup(context, accountID, url, d)); } public static boolean acctMatches(String accountID, String acct, String queriedUsername, @Nullable String queriedDomain) { @@ -1143,9 +1145,9 @@ public class UiUtils { () -> go.accept(null, null) ); } - public static void lookupAccountHandle(Context context, String accountID, Pair> queryHandle, BiConsumer, Bundle> go) { + public static MastodonAPIRequest lookupAccountHandle(Context context, String accountID, Pair> queryHandle, BiConsumer, Bundle> go) { String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse("")); - new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true) + return new GetSearchResults(fullHandle, GetSearchResults.Type.ACCOUNTS, true) .setCallback(new Callback<>() { @Override public void onSuccess(SearchResults results) { @@ -1176,12 +1178,12 @@ public class UiUtils { }).exec(accountID); } - public static void lookupURL(Context context, String accountID, String url, boolean launchBrowser, BiConsumer, Bundle> go) { + public static MastodonAPIRequest lookupURL(Context context, String accountID, String url, boolean launchBrowser, BiConsumer, Bundle> go) { Uri uri = Uri.parse(url); List path = uri.getPathSegments(); if (accountID != null && "https".equals(uri.getScheme())) { if (path.size() == 2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$") && AccountSessionManager.getInstance().getAccount(accountID).domain.equalsIgnoreCase(uri.getAuthority())) { - new GetStatusByID(path.get(1)) + return new GetStatusByID(path.get(1)) .setCallback(new Callback<>() { @Override public void onSuccess(Status result) { @@ -1198,12 +1200,9 @@ public class UiUtils { go.accept(null, null); } }) - .wrapProgress((Activity) context, R.string.loading, true, - d -> transformDialogForLookup(context, accountID, url, d)) .exec(accountID); - return; } else if (looksLikeFediverseUrl(url)) { - new GetSearchResults(url, null, true) + return new GetSearchResults(url, null, true) .setCallback(new Callback<>() { @Override public void onSuccess(SearchResults results) { @@ -1233,14 +1232,12 @@ public class UiUtils { go.accept(null, null); } }) - .wrapProgress((Activity) context, R.string.loading, true, - d -> transformDialogForLookup(context, accountID, url, d)) .exec(accountID); - return; } } if (launchBrowser) launchWebBrowser(context, url); go.accept(null, null); + return null; } public static void copyText(View v, String text) { From f31205c6708de2938f75efa4f151a4b31b360a67 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 15:45:22 +0200 Subject: [PATCH 62/79] generalize lookup error handling --- .../android/ui/utils/UiUtils.java | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index dc538f3a8..e9adca7ec 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -1092,10 +1092,6 @@ public class UiUtils { .exec(targetAccountID); } - public static void openURL(Context context, String accountID, String url) { - openURL(context, accountID, url, true); - } - public static void transformDialogForLookup(Context context, String accountID, @Nullable String url, ProgressDialog dialog) { if (accountID != null) { dialog.setTitle(context.getString(R.string.sk_loading_resource_on_instance_title, getInstanceName(accountID))); @@ -1111,9 +1107,32 @@ public class UiUtils { } } + private static Bundle bundleError(String error) { + Bundle args = new Bundle(); + args.putString("error", error); + return args; + } + + private static Bundle bundleError(ErrorResponse error) { + Bundle args = new Bundle(); + if (error instanceof MastodonErrorResponse e) { + args.putString("error", e.error); + args.putInt("httpStatus", e.httpStatus); + } + return args; + } + + public static void openURL(Context context, String accountID, String url) { + openURL(context, accountID, url, true); + } + public static void openURL(Context context, String accountID, String url, boolean launchBrowser) { lookupURL(context, accountID, url, launchBrowser, (clazz, args) -> { - if (clazz == null) return; + if (clazz == null) { + if (args.containsKey("error")) Toast.makeText(context, args.getString("error"), Toast.LENGTH_SHORT).show(); + if (launchBrowser) launchWebBrowser(context, url); + return; + } Nav.go((Activity) context, clazz, args); }).wrapProgress((Activity) context, R.string.loading, true, d -> transformDialogForLookup(context, accountID, url, d)); @@ -1161,19 +1180,12 @@ public class UiUtils { go.accept(ProfileFragment.class, args); return; } - Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show(); - args.putString("error", context.getString(R.string.sk_resource_not_found)); - go.accept(null, null); + go.accept(null, bundleError(context.getString(R.string.sk_resource_not_found))); } @Override public void onError(ErrorResponse error) { - Bundle args = new Bundle(); - if (error instanceof MastodonErrorResponse e) { - args.putString("error", e.error); - args.putInt("httpStatus", e.httpStatus); - } - go.accept(null, args); + go.accept(null, bundleError(error)); } }).exec(accountID); } @@ -1195,9 +1207,7 @@ public class UiUtils { @Override public void onError(ErrorResponse error) { - error.showToast(context); - if (launchBrowser) launchWebBrowser(context, url); - go.accept(null, null); + go.accept(null, bundleError(error)); } }) .exec(accountID); @@ -1221,15 +1231,12 @@ public class UiUtils { return; } if (launchBrowser) launchWebBrowser(context, url); - Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show(); - go.accept(null, null); + go.accept(null, bundleError(context.getString(R.string.sk_resource_not_found))); } @Override public void onError(ErrorResponse error) { - error.showToast(context); - if (launchBrowser) launchWebBrowser(context, url); - go.accept(null, null); + go.accept(null, bundleError(error)); } }) .exec(accountID); From 71884ab760162654cada83430095096578cdee82 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 15:50:30 +0200 Subject: [PATCH 63/79] don't show wrong relationship with remote accounts --- .../android/fragments/account_list/BaseAccountListFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java index bcf70060f..fb4b95556 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/account_list/BaseAccountListFragment.java @@ -257,7 +257,7 @@ public abstract class BaseAccountListFragment extends RecyclerFragment Date: Wed, 7 Jun 2023 16:09:41 +0200 Subject: [PATCH 64/79] change profile loading error behavior --- .../joinmastodon/android/fragments/ProfileFragment.java | 8 ++++---- mastodon/src/main/res/values-de-rDE/strings_sk.xml | 2 +- mastodon/src/main/res/values/strings_sk.xml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index 9f1c95296..c637429e3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -384,10 +384,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList UiUtils.lookupAccountHandle(getContext(), accountID, remoteAccount.getFullyQualifiedName(), (c, args) -> { if (getContext() == null) return; if (args == null || !args.containsKey("profileAccount")) { - onError(new MastodonErrorResponse( - getContext().getString(R.string.sk_error_loading_profile), - 0, null - )); + Toast.makeText(getContext(), getContext().getString( + R.string.sk_error_loading_profile, domain + ), Toast.LENGTH_SHORT).show(); + Nav.finish(this); return; } onAccountLoaded(Parcels.unwrap(args.getParcelable("profileAccount"))); diff --git a/mastodon/src/main/res/values-de-rDE/strings_sk.xml b/mastodon/src/main/res/values-de-rDE/strings_sk.xml index 4e972f161..9d2cdcad6 100644 --- a/mastodon/src/main/res/values-de-rDE/strings_sk.xml +++ b/mastodon/src/main/res/values-de-rDE/strings_sk.xml @@ -294,6 +294,6 @@ Dadurch lässt beim Erstellen von Beiträgen ein Inhaltstyp wie Markdown angeben. Nicht alle Instanzen unterstützen das. Infos von Remote-Instanzen laden keine Remote-Infos abrufbar - Konnte das Profil auf deiner Heim-Instanz nicht laden. + Konnte das Profil via %s nicht laden Für vollständigere Auflistung von Follower*innen, Likes und Boosts können die Informationen von der Ursprungs-Instanz geladen werden. \ No newline at end of file diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index 221baaac9..40dd203a8 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -294,7 +294,7 @@ Share with account Share or open with account remote info unavailable - Failed loading the profile on your home instance. + Failed loading the profile via %s Load info from remote instances Try fetching more accurate listings for followers, likes and boosts by loading the information from the instance of origin. \ No newline at end of file From 2ea7333daaafbf238d8977b9321e92582191ede3 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 18:45:50 +0200 Subject: [PATCH 65/79] only reload main status when refreshing closes sk22#552 --- .../org/joinmastodon/android/fragments/ThreadFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 15ebb3de0..9c5bed3b1 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import org.joinmastodon.android.E; +import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.statuses.GetStatusByID; import org.joinmastodon.android.api.requests.statuses.GetStatusContext; @@ -111,7 +112,7 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist @Override protected void doLoadData(int offset, int count){ - loadMainStatus(); + if (refreshing) loadMainStatus(); currentRequest=new GetStatusContext(mainStatus.id) .setCallback(new SimpleCallback<>(this){ @Override From 4a0e4edef815869603e6e577d2a1637da1aeb43d Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 20:28:56 +0200 Subject: [PATCH 66/79] load more if screen isn't full yet --- .../android/fragments/BaseStatusListFragment.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 5ae8a2d0f..debbf06f2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -754,6 +754,13 @@ public abstract class BaseStatusListFragment exten assistContent.setWebUri(getWebUri(getSession().getInstanceUri().buildUpon())); } + @Override + protected void onDataLoaded(List d, boolean more) { + super.onDataLoaded(d, more); + // more available, but the page isn't even full yet? seems wrong, let's load some more + if (more && d.size() < itemsPerPage) preloader.onScrolledToLastItem(); + } + protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter> implements ImageLoaderRecyclerAdapter{ public DisplayItemsAdapter(){ From cd46ed565f2cd2c487c3412c9eaaa136094cf73a Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 21:11:20 +0200 Subject: [PATCH 67/79] open browser if login redirects to website --- .../java/org/joinmastodon/android/OAuthActivity.java | 3 +++ .../android/api/MastodonAPIController.java | 6 ++++++ .../joinmastodon/android/api/MastodonAPIRequest.java | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/OAuthActivity.java b/mastodon/src/main/java/org/joinmastodon/android/OAuthActivity.java index 553074599..f239bf3f0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/OAuthActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/OAuthActivity.java @@ -61,6 +61,9 @@ public class OAuthActivity extends Activity{ @Override public void onSuccess(Token token){ new GetOwnAccount() + // in case the instance (looking at pixelfed) wants to redirect to a + // website, we need to pass a context so we can launch a browser + .setContext(OAuthActivity.this) .setCallback(new Callback<>(){ @Override public void onSuccess(Account account){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java index 32b7cad8d..b941a485f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java @@ -17,6 +17,7 @@ import org.joinmastodon.android.api.gson.IsoInstantTypeAdapter; import org.joinmastodon.android.api.gson.IsoLocalDateTypeAdapter; import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.model.Status; +import org.joinmastodon.android.ui.utils.UiUtils; import java.io.BufferedReader; import java.io.IOException; @@ -161,6 +162,11 @@ public class MastodonAPIController{ respObj=gson.fromJson(reader, req.respClass); } }catch(JsonIOException|JsonSyntaxException x){ + if (req.context != null && response.body().contentType().subtype().equals("html")) { + UiUtils.launchWebBrowser(req.context, response.request().url().toString()); + req.cancel(); + return; + } if(BuildConfig.DEBUG) Log.w(TAG, "["+(session==null ? "no-auth" : session.getID())+"] "+response+" error parsing or reading body", x); req.onError(x.getLocalizedMessage(), response.code(), x); diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java index f9dd1ee72..b6d624588 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIRequest.java @@ -2,6 +2,7 @@ package org.joinmastodon.android.api; import android.app.Activity; import android.app.ProgressDialog; +import android.content.Context; import android.net.Uri; import android.util.Log; import android.util.Pair; @@ -50,6 +51,7 @@ public abstract class MastodonAPIRequest extends APIRequest{ Map headers; private ProgressDialog progressDialog; protected boolean removeUnsupportedItems; + @Nullable Context context; public MastodonAPIRequest(HttpMethod method, String path, Class respClass){ this.path=path; @@ -181,6 +183,16 @@ public abstract class MastodonAPIRequest extends APIRequest{ return this; } + public MastodonAPIRequest setContext(Context context) { + this.context = context; + return this; + } + + @Nullable + public Context getContext() { + return context; + } + @CallSuper public void validateAndPostprocessResponse(T respObj, Response httpResponse) throws IOException{ if(respObj instanceof BaseModel){ From b93b1847c3bb37ae47c5f3148ee01c7918b25972 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 21:12:30 +0200 Subject: [PATCH 68/79] increase pixelfed compatibility --- .../android/fragments/ComposeFragment.java | 32 +++++++++++++++---- .../android/fragments/HasAccountID.java | 4 +++ .../android/fragments/ProfileFragment.java | 4 ++- .../joinmastodon/android/model/Account.java | 1 - .../joinmastodon/android/model/Instance.java | 4 +++ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index cc6f334b6..7fce1101b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -149,7 +149,7 @@ import me.grishka.appkit.imageloader.ViewImageLoader; import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; import me.grishka.appkit.utils.V; -public class ComposeFragment extends MastodonToolbarFragment implements OnBackPressedListener, ComposeEditText.SelectionListener{ +public class ComposeFragment extends MastodonToolbarFragment implements OnBackPressedListener, ComposeEditText.SelectionListener, HasAccountID { private static final int MEDIA_RESULT=717; private static final int IMAGE_DESCRIPTION_RESULT=363; @@ -355,6 +355,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } else { mediaBtn.setOnClickListener(v -> openFilePicker(false)); } + if (isInstancePixelfed()) pollBtn.setVisibility(View.GONE); pollBtn.setOnClickListener(v->togglePoll()); emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText)); spoilerBtn.setOnClickListener(v->toggleSpoiler()); @@ -847,12 +848,18 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null); buildLanguageSelector(languageButton); - if (editingStatus != null && scheduledStatus == null) { + if (isInstancePixelfed() || (editingStatus != null && scheduledStatus == null)) { // editing an already published post draftsBtn.setVisibility(View.GONE); + spoilerBtn.setVisibility(View.GONE); } } + @Override + public String getAccountID() { + return accountID; + } + private void navigateToUnsentPosts() { Bundle args=new Bundle(); args.putString("account", accountID); @@ -1009,7 +1016,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr if(att.state!=AttachmentUploadState.DONE) nonDoneAttachmentCount++; } - publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1)); + publishButton.setEnabled((!isInstancePixelfed() || attachments.size() > 0) && (trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1)); sendError.setVisibility(View.GONE); } @@ -1151,7 +1158,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr sendProgress.setVisibility(View.VISIBLE); sendError.setVisibility(View.GONE); - Callback resCallback=new Callback<>(){ + Callback resCallback = new Callback<>(){ @Override public void onSuccess(Status result){ maybeDeleteScheduledPost(() -> { @@ -1164,7 +1171,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr E.post(new StatusCountersUpdatedEvent(replyTo)); } }else{ - E.post(new StatusUpdatedEvent(result)); + // pixelfed doesn't return the edited status :/ + Status editedStatus = result == null ? editingStatus : result; + if (result == null) { + editedStatus.text = req.status; + editedStatus.spoilerText = req.spoilerText; + editedStatus.sensitive = req.sensitive; + editedStatus.language = req.language; + // user will have to reload to see html + editedStatus.content = req.status; + } + E.post(new StatusUpdatedEvent(editedStatus)); } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !isStateSaved()) { Nav.finish(ComposeFragment.this); @@ -1899,9 +1916,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr visibilityPopup=new PopupMenu(getActivity(), v); visibilityPopup.inflate(R.menu.compose_visibility); Menu m=visibilityPopup.getMenu(); + if (isInstancePixelfed()) { + m.findItem(R.id.vis_private).setVisible(false); + } MenuItem localOnlyItem = visibilityPopup.getMenu().findItem(R.id.local_only); boolean prefsSaysSupported = GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID); - if (instance.isAkkoma()) { + if (isInstanceAkkoma()) { m.findItem(R.id.vis_local).setVisible(true); } else if (localOnly || prefsSaysSupported) { localOnlyItem.setVisible(true); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HasAccountID.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HasAccountID.java index 67a3bbfac..5e7f01fed 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HasAccountID.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HasAccountID.java @@ -17,6 +17,10 @@ public interface HasAccountID { return getInstance().map(Instance::isAkkoma).orElse(false); } + default boolean isInstancePixelfed() { + return getInstance().map(Instance::isPixelfed).orElse(false); + } + default Optional getInstance() { return getSession().getInstance(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index c637429e3..e40588491 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -654,8 +654,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList )); } menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername())); - if(isOwnProfile) + if(isOwnProfile) { + if (isInstancePixelfed()) menu.findItem(R.id.scheduled).setVisible(false); return; + } MenuItem mute = menu.findItem(R.id.mute); mute.setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getShortUsername())); diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Account.java b/mastodon/src/main/java/org/joinmastodon/android/model/Account.java index 1fdfe08cc..99be28f88 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Account.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Account.java @@ -65,7 +65,6 @@ public class Account extends BaseModel implements Searchable{ /** * An image banner that is shown above the profile and in profile cards. */ - @RequiredField public String header; /** * A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF. diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Instance.java b/mastodon/src/main/java/org/joinmastodon/android/model/Instance.java index 12707952f..e5b6f8719 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Instance.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Instance.java @@ -148,6 +148,10 @@ public class Instance extends BaseModel{ return pleroma != null; } + public boolean isPixelfed() { + return version.contains("compatible; Pixelfed"); + } + public boolean hasFeature(Feature feature) { Optional> pleromaFeatures = Optional.ofNullable(pleroma) .map(p -> p.metadata) From d35ec18a88f7037ecdadef66c316ce50045805c6 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 21:12:38 +0200 Subject: [PATCH 69/79] increase akkoma scheduled posts compatibility --- .../android/model/ScheduledStatus.java | 21 ++++++++++++++++--- .../joinmastodon/android/model/Status.java | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/ScheduledStatus.java b/mastodon/src/main/java/org/joinmastodon/android/model/ScheduledStatus.java index ebc56c8c7..309bb4646 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/ScheduledStatus.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/ScheduledStatus.java @@ -1,5 +1,6 @@ package org.joinmastodon.android.model; +import org.joinmastodon.android.api.ObjectValidationException; import org.joinmastodon.android.api.RequiredField; import org.joinmastodon.android.model.Poll.Option; import org.parceler.Parcel; @@ -16,7 +17,6 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{ public Instant scheduledAt; @RequiredField public Params params; - @RequiredField public List mediaAttachments; @Override @@ -24,8 +24,17 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{ return id; } + @Override + public void postprocess() throws ObjectValidationException { + super.postprocess(); + if (mediaAttachments == null) mediaAttachments = List.of(); + for(Attachment a:mediaAttachments) + a.postprocess(); + if (params != null) params.postprocess(); + } + @Parcel - public static class Params { + public static class Params extends BaseModel { @RequiredField public String text; public String spoilerText; @@ -40,10 +49,16 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{ public String applicationId; public List mediaIds; public ContentType contentType; + + @Override + public void postprocess() throws ObjectValidationException { + super.postprocess(); + if (poll != null) poll.postprocess(); + } } @Parcel - public static class ScheduledPoll { + public static class ScheduledPoll extends BaseModel { @RequiredField public String expiresIn; @RequiredField diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java index 1849b5b07..59f64df6d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -39,7 +39,6 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ public boolean sensitive; @RequiredField public String spoilerText; - @RequiredField public List mediaAttachments; public Application application; @RequiredField @@ -96,6 +95,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ t.postprocess(); for(Emoji e:emojis) e.postprocess(); + if (mediaAttachments == null) mediaAttachments = List.of(); for(Attachment a:mediaAttachments) a.postprocess(); account.postprocess(); From 1f63401e5b6c87a6cf4a4ed8e3f78c4588d6f285 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 21:50:50 +0200 Subject: [PATCH 70/79] fix pixelfed post editing --- .../android/ui/displayitems/HeaderStatusDisplayItem.java | 8 +++++++- .../java/org/joinmastodon/android/ui/text/HtmlParser.java | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java index 5a30834f3..35c136221 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java @@ -196,7 +196,13 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ args.putBoolean("navigateToStatus", true); } } - if(!redraft && TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText)){ + boolean isPixelfed = item.parentFragment.isInstancePixelfed(); + boolean textEmpty = TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText); + if(!redraft && (isPixelfed || textEmpty)){ + if (isPixelfed) { + args.putString("sourceText", HtmlParser.text(item.status.content)); + args.putString("sourceSpoiler", item.status.spoilerText); + } Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args); }else if(item.scheduledStatus!=null){ args.putString("sourceText", item.status.text); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java index 2e6001832..ab58a0480 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java @@ -251,6 +251,10 @@ public class HtmlParser{ return Jsoup.clean(html, Safelist.none()); } + public static String text(String html) { + return Jsoup.parse(html).body().wholeText(); + } + public static CharSequence parseLinks(String text){ Matcher matcher=URL_PATTERN.matcher(text); if(!matcher.find()) // Return the original string if there are no URLs From 9612248695c39af1656815ff48fe94a772251e04 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 21:51:17 +0200 Subject: [PATCH 71/79] remove unused imports --- .../java/org/joinmastodon/android/ui/text/HtmlParser.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java index ab58a0480..cf65931fe 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/text/HtmlParser.java @@ -1,14 +1,10 @@ package org.joinmastodon.android.ui.text; import android.graphics.Typeface; -import android.graphics.fonts.FontFamily; -import android.graphics.fonts.FontStyle; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; -import android.text.style.BackgroundColorSpan; import android.text.style.BulletSpan; -import android.text.style.ForegroundColorSpan; import android.text.style.LeadingMarginSpan; import android.text.style.RelativeSizeSpan; import android.text.style.StrikethroughSpan; @@ -17,13 +13,10 @@ import android.text.style.SubscriptSpan; import android.text.style.SuperscriptSpan; import android.text.style.TypefaceSpan; import android.text.style.UnderlineSpan; -import android.util.TypedValue; import android.widget.TextView; import com.twitter.twittertext.Regex; -import org.joinmastodon.android.MastodonApp; -import org.joinmastodon.android.R; import org.joinmastodon.android.model.Emoji; import org.joinmastodon.android.model.Hashtag; import org.joinmastodon.android.model.Mention; From 307d483a56cb16278839361d25fc3085e3c4fa23 Mon Sep 17 00:00:00 2001 From: sk Date: Wed, 7 Jun 2023 21:59:56 +0200 Subject: [PATCH 72/79] add comment --- .../android/ui/displayitems/HeaderStatusDisplayItem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java index 35c136221..4e4eb4a9e 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/HeaderStatusDisplayItem.java @@ -199,6 +199,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{ boolean isPixelfed = item.parentFragment.isInstancePixelfed(); boolean textEmpty = TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText); if(!redraft && (isPixelfed || textEmpty)){ + // pixelfed doesn't support /statuses/:id/source :/ if (isPixelfed) { args.putString("sourceText", HtmlParser.text(item.status.content)); args.putString("sourceSpoiler", item.status.spoilerText); From 1ab953d8191f2fb710ff19eefb3a495b93135aad Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 8 Jun 2023 15:03:04 +0200 Subject: [PATCH 73/79] fix spoiler button being hidden while editing closes sk22#553 --- .../org/joinmastodon/android/fragments/ComposeFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index 7fce1101b..8ddaebbf8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -848,10 +848,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null); buildLanguageSelector(languageButton); + if (isInstancePixelfed()) spoilerBtn.setVisibility(View.GONE); if (isInstancePixelfed() || (editingStatus != null && scheduledStatus == null)) { // editing an already published post draftsBtn.setVisibility(View.GONE); - spoilerBtn.setVisibility(View.GONE); } } From 97547f334f16db1000e1399a55b7088953e77be4 Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 8 Jun 2023 15:31:58 +0200 Subject: [PATCH 74/79] returning optionals for the optional god closes sk22#555 --- .../android/ExternalShareActivity.java | 4 +- .../android/ui/utils/UiUtils.java | 75 ++++++++++--------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java index 40418d5fe..968d786a3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java @@ -64,8 +64,8 @@ public class ExternalShareActivity extends FragmentStackActivity{ fediHandle .>map(handle -> UiUtils.lookupAccountHandle(this, accountId, handle, callback)) - .or(() -> Optional.ofNullable( - UiUtils.lookupURL(this, accountId, text.get(), false, callback))) + .or(() -> + UiUtils.lookupURL(this, accountId, text.get(), false, callback)) .ifPresent(req -> req.wrapProgress(this, R.string.loading, true, d -> { UiUtils.transformDialogForLookup(this, accountId, isFediUrl ? text.get() : null, d); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index e9adca7ec..0c9dd3fa2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -1053,43 +1053,43 @@ public class UiUtils { }, null); } - public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer resultConsumer) { - lookup(context, queryStatus, targetAccountID, sourceAccountID, GetSearchResults.Type.STATUSES, resultConsumer, results -> + public static Optional> lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer resultConsumer) { + return lookup(context, queryStatus, targetAccountID, sourceAccountID, GetSearchResults.Type.STATUSES, resultConsumer, results -> !results.statuses.isEmpty() ? Optional.of(results.statuses.get(0)) : Optional.empty() ); } - public static void lookupAccount(Context context, Account queryAccount, String targetAccountID, @Nullable String sourceAccountID, Consumer resultConsumer) { - lookup(context, queryAccount, targetAccountID, sourceAccountID, GetSearchResults.Type.ACCOUNTS, resultConsumer, results -> + public static Optional> lookupAccount(Context context, Account queryAccount, String targetAccountID, @Nullable String sourceAccountID, Consumer resultConsumer) { + return lookup(context, queryAccount, targetAccountID, sourceAccountID, GetSearchResults.Type.ACCOUNTS, resultConsumer, results -> !results.accounts.isEmpty() ? Optional.of(results.accounts.get(0)) : Optional.empty() ); } - public static void lookup(Context context, T query, String targetAccountID, @Nullable String sourceAccountID, @Nullable GetSearchResults.Type type, Consumer resultConsumer, Function> extractResult) { + public static Optional> lookup(Context context, T query, String targetAccountID, @Nullable String sourceAccountID, @Nullable GetSearchResults.Type type, Consumer resultConsumer, Function> extractResult) { if (sourceAccountID != null && targetAccountID.startsWith(sourceAccountID.substring(0, sourceAccountID.indexOf('_')))) { resultConsumer.accept(query); - return; + return Optional.empty(); } - new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() { - @Override - public void onSuccess(SearchResults results) { - Optional result = extractResult.apply(results); - if (result.isPresent()) resultConsumer.accept(result.get()); - else { - Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show(); - resultConsumer.accept(null); - } - } + return Optional.of(new GetSearchResults(query.getQuery(), type, true).setCallback(new Callback<>() { + @Override + public void onSuccess(SearchResults results) { + Optional result = extractResult.apply(results); + if (result.isPresent()) resultConsumer.accept(result.get()); + else { + Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show(); + resultConsumer.accept(null); + } + } - @Override - public void onError(ErrorResponse error) { - error.showToast(context); - } - }) + @Override + public void onError(ErrorResponse error) { + error.showToast(context); + } + }) .wrapProgress((Activity) context, R.string.loading, true, d -> transformDialogForLookup(context, targetAccountID, null, d)) - .exec(targetAccountID); + .exec(targetAccountID)); } public static void transformDialogForLookup(Context context, String accountID, @Nullable String url, ProgressDialog dialog) { @@ -1129,13 +1129,13 @@ public class UiUtils { public static void openURL(Context context, String accountID, String url, boolean launchBrowser) { lookupURL(context, accountID, url, launchBrowser, (clazz, args) -> { if (clazz == null) { - if (args.containsKey("error")) Toast.makeText(context, args.getString("error"), Toast.LENGTH_SHORT).show(); + if (args != null && args.containsKey("error")) Toast.makeText(context, args.getString("error"), Toast.LENGTH_SHORT).show(); if (launchBrowser) launchWebBrowser(context, url); return; } Nav.go((Activity) context, clazz, args); - }).wrapProgress((Activity) context, R.string.loading, true, d -> - transformDialogForLookup(context, accountID, url, d)); + }).map(req -> req.wrapProgress((Activity) context, R.string.loading, true, d -> + transformDialogForLookup(context, accountID, url, d))); } public static boolean acctMatches(String accountID, String acct, String queriedUsername, @Nullable String queriedDomain) { @@ -1158,11 +1158,13 @@ public class UiUtils { } } - public static void lookupAccountHandle(Context context, String accountID, String query, BiConsumer, Bundle> go) { - parseFediverseHandle(query).ifPresentOrElse( - handle -> lookupAccountHandle(context, accountID, handle, go), - () -> go.accept(null, null) - ); + public static Optional> lookupAccountHandle(Context context, String accountID, String query, BiConsumer, Bundle> go) { + return parseFediverseHandle(query).map( + handle -> lookupAccountHandle(context, accountID, handle, go)) + .or(() -> { + go.accept(null, null); + return Optional.empty(); + }); } public static MastodonAPIRequest lookupAccountHandle(Context context, String accountID, Pair> queryHandle, BiConsumer, Bundle> go) { String fullHandle = ("@" + queryHandle.first) + (queryHandle.second.map(domain -> "@" + domain).orElse("")); @@ -1190,12 +1192,12 @@ public class UiUtils { }).exec(accountID); } - public static MastodonAPIRequest lookupURL(Context context, String accountID, String url, boolean launchBrowser, BiConsumer, Bundle> go) { + public static Optional> lookupURL(Context context, String accountID, String url, boolean launchBrowser, BiConsumer, Bundle> go) { Uri uri = Uri.parse(url); List path = uri.getPathSegments(); if (accountID != null && "https".equals(uri.getScheme())) { if (path.size() == 2 && path.get(0).matches("^@[a-zA-Z0-9_]+$") && path.get(1).matches("^[0-9]+$") && AccountSessionManager.getInstance().getAccount(accountID).domain.equalsIgnoreCase(uri.getAuthority())) { - return new GetStatusByID(path.get(1)) + return Optional.of(new GetStatusByID(path.get(1)) .setCallback(new Callback<>() { @Override public void onSuccess(Status result) { @@ -1210,9 +1212,9 @@ public class UiUtils { go.accept(null, bundleError(error)); } }) - .exec(accountID); + .exec(accountID)); } else if (looksLikeFediverseUrl(url)) { - return new GetSearchResults(url, null, true) + return Optional.of(new GetSearchResults(url, null, true) .setCallback(new Callback<>() { @Override public void onSuccess(SearchResults results) { @@ -1239,12 +1241,11 @@ public class UiUtils { go.accept(null, bundleError(error)); } }) - .exec(accountID); + .exec(accountID)); } } - if (launchBrowser) launchWebBrowser(context, url); go.accept(null, null); - return null; + return Optional.empty(); } public static void copyText(View v, String text) { From 6f3fd4d45459a1a094d375747f534b836c107eca Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 9 Jun 2023 12:47:32 +0200 Subject: [PATCH 75/79] fix opening browser twice closes sk22#559 --- .../java/org/joinmastodon/android/ExternalShareActivity.java | 2 +- .../main/java/org/joinmastodon/android/ui/utils/UiUtils.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java index 968d786a3..ce56efb54 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ExternalShareActivity.java @@ -65,7 +65,7 @@ public class ExternalShareActivity extends FragmentStackActivity{ .>map(handle -> UiUtils.lookupAccountHandle(this, accountId, handle, callback)) .or(() -> - UiUtils.lookupURL(this, accountId, text.get(), false, callback)) + UiUtils.lookupURL(this, accountId, text.get(), callback)) .ifPresent(req -> req.wrapProgress(this, R.string.loading, true, d -> { UiUtils.transformDialogForLookup(this, accountId, isFediUrl ? text.get() : null, d); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 0c9dd3fa2..385476a27 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -1127,7 +1127,7 @@ public class UiUtils { } public static void openURL(Context context, String accountID, String url, boolean launchBrowser) { - lookupURL(context, accountID, url, launchBrowser, (clazz, args) -> { + lookupURL(context, accountID, url, (clazz, args) -> { if (clazz == null) { if (args != null && args.containsKey("error")) Toast.makeText(context, args.getString("error"), Toast.LENGTH_SHORT).show(); if (launchBrowser) launchWebBrowser(context, url); @@ -1192,7 +1192,7 @@ public class UiUtils { }).exec(accountID); } - public static Optional> lookupURL(Context context, String accountID, String url, boolean launchBrowser, BiConsumer, Bundle> go) { + public static Optional> lookupURL(Context context, String accountID, String url, BiConsumer, Bundle> go) { Uri uri = Uri.parse(url); List path = uri.getPathSegments(); if (accountID != null && "https".equals(uri.getScheme())) { @@ -1232,7 +1232,6 @@ public class UiUtils { go.accept(ProfileFragment.class, args); return; } - if (launchBrowser) launchWebBrowser(context, url); go.accept(null, bundleError(context.getString(R.string.sk_resource_not_found))); } From ba7aeb358b7b7d9d2a762dbe8e3db23dd05b2a09 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 9 Jun 2023 12:50:00 +0200 Subject: [PATCH 76/79] fix thread status not clickable if filter revealed closes sk22#554 --- .../java/org/joinmastodon/android/fragments/ThreadFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 9c5bed3b1..9064475a5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -319,7 +319,7 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist @Override public boolean isItemEnabled(String id){ - return !id.equals(mainStatus.id); + return !id.equals(mainStatus.id) || !mainStatus.filterRevealed; } @Override From c4238fb19bb7706017c16b4c64219c189126b900 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 9 Jun 2023 13:27:25 +0200 Subject: [PATCH 77/79] keep revealed states when reloading closes sk22#561 --- .../android/fragments/ThreadFragment.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 9064475a5..5826d3470 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -8,7 +8,6 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import org.joinmastodon.android.E; -import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.statuses.GetStatusByID; import org.joinmastodon.android.api.requests.statuses.GetStatusContext; @@ -38,6 +37,7 @@ import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -118,7 +118,10 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist @Override public void onSuccess(StatusContext result){ if (getContext() == null) return; + Map oldData = null; if(refreshing){ + oldData = new HashMap<>(data.size()); + for (Status s : data) oldData.put(s.id, s); data.clear(); ancestryMap.clear(); displayItems.clear(); @@ -150,6 +153,18 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist adapter.notifyItemRemoved(prependedCount); count--; } + + // restore previous spoiler/filter revealed states when refreshing + if (refreshing && oldData.size() > 0) { + for (Status s : data) { + Status oldStatus = oldData.get(s.id); + if (oldStatus != null) { + s.spoilerRevealed = oldStatus.spoilerRevealed; + s.filterRevealed = oldStatus.filterRevealed; + } + } + } + dataLoaded(); if(refreshing){ refreshDone(); @@ -188,6 +203,10 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist protected Object maybeApplyMainStatus() { if (updatedStatus == null || !contextInitiallyRendered) return null; + // restore revealed states for main status because it gets updated after doLoadData + updatedStatus.filterRevealed = mainStatus.filterRevealed; + updatedStatus.spoilerRevealed = mainStatus.spoilerRevealed; + // returning fired event object to facilitate testing Object event; if (updatedStatus.editedAt != null && From 870bfaf08cac0739294760fee17d047c8ee24511 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 9 Jun 2023 14:50:21 +0200 Subject: [PATCH 78/79] don't use switch for android ids --- .../android/fragments/SettingsFragment.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index fe1d8edea..2e73e7805 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -560,14 +560,14 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide private boolean onContentTypeChanged(MenuItem item, Button btn){ int id = item.getItemId(); - ContentType contentType = switch (id) { - case R.id.content_type_plain -> ContentType.PLAIN; - case R.id.content_type_html -> ContentType.HTML; - case R.id.content_type_markdown -> ContentType.MARKDOWN; - case R.id.content_type_bbcode -> ContentType.BBCODE; - case R.id.content_type_misskey_markdown -> ContentType.MISSKEY_MARKDOWN; - default -> null; - }; + + ContentType contentType = null; + if (id == R.id.content_type_plain) contentType = ContentType.PLAIN; + else if (id == R.id.content_type_html) contentType = ContentType.HTML; + else if (id == R.id.content_type_markdown) contentType = ContentType.MARKDOWN; + else if (id == R.id.content_type_bbcode) contentType = ContentType.BBCODE; + else if (id == R.id.content_type_misskey_markdown) contentType = ContentType.MISSKEY_MARKDOWN; + GlobalUserPreferences.accountsDefaultContentTypes.put(accountID, contentType); GlobalUserPreferences.save(); btn.setText(getContentTypeString(contentType)); From 35bf858a83189ea35e4d6edc7678df19a6ec000e Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 9 Jun 2023 14:54:03 +0200 Subject: [PATCH 79/79] auto-reveal equal spoilers in threads --- .../android/GlobalUserPreferences.java | 9 ++++ .../android/fragments/SettingsFragment.java | 48 +++++++++++++++++-- .../android/fragments/ThreadFragment.java | 21 +++++--- .../ic_fluent_star_off_24_regular.xml | 3 ++ .../res/menu/settings_auto_reveal_spoiler.xml | 12 +++++ mastodon/src/main/res/values/strings_sk.xml | 5 ++ 6 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 mastodon/src/main/res/drawable/ic_fluent_star_off_24_regular.xml create mode 100644 mastodon/src/main/res/menu/settings_auto_reveal_spoiler.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java index 3366f213f..de73cdfc9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java +++ b/mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java @@ -49,6 +49,7 @@ public class GlobalUserPreferences{ public static boolean compactReblogReplyLine; public static boolean confirmBeforeReblog; public static boolean allowRemoteLoading; + public static AutoRevealMode autoRevealEqualSpoilers; public static String publishButtonText; public static ThemePreference theme; public static ColorPreference color; @@ -129,6 +130,7 @@ public class GlobalUserPreferences{ accountsWithContentTypesEnabled=prefs.getStringSet("accountsWithContentTypesEnabled", new HashSet<>()); accountsDefaultContentTypes=fromJson(prefs.getString("accountsDefaultContentTypes", null), accountsDefaultContentTypesType, new HashMap<>()); allowRemoteLoading=prefs.getBoolean("allowRemoteLoading", true); + autoRevealEqualSpoilers=AutoRevealMode.valueOf(prefs.getString("autoRevealEqualSpoilers", AutoRevealMode.THREADS.name())); try { color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name())); @@ -179,6 +181,7 @@ public class GlobalUserPreferences{ .putStringSet("accountsWithContentTypesEnabled", accountsWithContentTypesEnabled) .putString("accountsDefaultContentTypes", gson.toJson(accountsDefaultContentTypes)) .putBoolean("allowRemoteLoading", allowRemoteLoading) + .putString("autoRevealEqualSpoilers", autoRevealEqualSpoilers.name()) .apply(); } @@ -198,4 +201,10 @@ public class GlobalUserPreferences{ LIGHT, DARK } + + public enum AutoRevealMode { + NEVER, + THREADS, + DISCUSSIONS + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java index 2e73e7805..1fbe3c427 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java @@ -37,6 +37,7 @@ import com.squareup.otto.Subscribe; import org.joinmastodon.android.BuildConfig; import org.joinmastodon.android.E; import org.joinmastodon.android.GlobalUserPreferences; +import org.joinmastodon.android.GlobalUserPreferences.AutoRevealMode; import org.joinmastodon.android.GlobalUserPreferences.ColorPreference; import org.joinmastodon.android.MainActivity; import org.joinmastodon.android.MastodonApp; @@ -85,8 +86,8 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide private ArrayList items=new ArrayList<>(); private ThemeItem themeItem; private NotificationPolicyItem notificationPolicyItem; - private SwitchItem showNewPostsItem, glitchModeItem, compactReblogReplyLineItem; - private ButtonItem defaultContentTypeButtonItem; + private SwitchItem showNewPostsItem, glitchModeItem, compactReblogReplyLineItem, alwaysRevealSpoilersItem; + private ButtonItem defaultContentTypeButtonItem, autoRevealSpoilersItem; private String accountID; private boolean needUpdateNotificationSettings; private boolean needAppRestart; @@ -189,9 +190,18 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide GlobalUserPreferences.showInteractionCounts=i.checked; GlobalUserPreferences.save(); })); - items.add(new SwitchItem(R.string.sk_settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{ + items.add(alwaysRevealSpoilersItem = new SwitchItem(R.string.sk_settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{ GlobalUserPreferences.alwaysExpandContentWarnings=i.checked; GlobalUserPreferences.save(); + if (list.findViewHolderForAdapterPosition(items.indexOf(autoRevealSpoilersItem)) instanceof ButtonViewHolder bvh) bvh.rebind(); + })); + items.add(autoRevealSpoilersItem = new ButtonItem(R.string.sk_settings_auto_reveal_equal_spoilers, R.drawable.ic_fluent_eye_24_regular, b->{ + PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL); + popupMenu.inflate(R.menu.settings_auto_reveal_spoiler); + popupMenu.setOnMenuItemClickListener(i -> onAutoRevealSpoilerClick(i, b)); + b.setOnTouchListener(popupMenu.getDragToOpenListener()); + b.setOnClickListener(v->popupMenu.show()); + onAutoRevealSpoilerChanged(b); })); items.add(new SwitchItem(R.string.sk_tabs_disable_swipe, R.drawable.ic_fluent_swipe_right_24_regular, GlobalUserPreferences.disableSwipe, i->{ GlobalUserPreferences.disableSwipe=i.checked; @@ -276,7 +286,7 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide GlobalUserPreferences.collapseLongPosts=i.checked; GlobalUserPreferences.save(); })); - items.add(new SwitchItem(R.string.sk_settings_hide_interaction, R.drawable.ic_fluent_eye_24_regular, GlobalUserPreferences.spectatorMode, i->{ + items.add(new SwitchItem(R.string.sk_settings_hide_interaction, R.drawable.ic_fluent_star_off_24_regular, GlobalUserPreferences.spectatorMode, i->{ GlobalUserPreferences.spectatorMode=i.checked; GlobalUserPreferences.save(); needAppRestart=true; @@ -531,6 +541,36 @@ public class SettingsFragment extends MastodonToolbarFragment implements Provide return true; } + private boolean onAutoRevealSpoilerClick(MenuItem item, Button btn) { + int id = item.getItemId(); + + AutoRevealMode mode = AutoRevealMode.NEVER; + if (id == R.id.auto_reveal_threads) mode = AutoRevealMode.THREADS; + else if (id == R.id.auto_reveal_discussions) mode = AutoRevealMode.DISCUSSIONS; + + GlobalUserPreferences.alwaysExpandContentWarnings = false; + GlobalUserPreferences.autoRevealEqualSpoilers = mode; + GlobalUserPreferences.save(); + onAutoRevealSpoilerChanged(btn); + return true; + } + + private void onAutoRevealSpoilerChanged(Button b) { + if (GlobalUserPreferences.alwaysExpandContentWarnings) { + b.setText(R.string.sk_settings_auto_reveal_always); + } else { + b.setText(switch(GlobalUserPreferences.autoRevealEqualSpoilers){ + case THREADS -> R.string.sk_settings_auto_reveal_threads; + case DISCUSSIONS -> R.string.sk_settings_auto_reveal_discussions; + default -> R.string.sk_settings_auto_reveal_never; + }); + if (alwaysRevealSpoilersItem.checked != GlobalUserPreferences.alwaysExpandContentWarnings) { + alwaysRevealSpoilersItem.checked = GlobalUserPreferences.alwaysExpandContentWarnings; + if (list.findViewHolderForAdapterPosition(items.indexOf(alwaysRevealSpoilersItem)) instanceof SwitchViewHolder svh) svh.rebind(); + } + } + } + private void onTrueBlackThemeChanged(SwitchItem item){ GlobalUserPreferences.trueBlackTheme=item.checked; GlobalUserPreferences.save(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 5826d3470..6ff24b7b7 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -8,6 +8,8 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import org.joinmastodon.android.E; +import org.joinmastodon.android.GlobalUserPreferences; +import org.joinmastodon.android.GlobalUserPreferences.AutoRevealMode; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.statuses.GetStatusByID; import org.joinmastodon.android.api.requests.statuses.GetStatusContext; @@ -154,13 +156,18 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist count--; } - // restore previous spoiler/filter revealed states when refreshing - if (refreshing && oldData.size() > 0) { - for (Status s : data) { - Status oldStatus = oldData.get(s.id); - if (oldStatus != null) { - s.spoilerRevealed = oldStatus.spoilerRevealed; - s.filterRevealed = oldStatus.filterRevealed; + for (Status s : data) { + Status oldStatus = oldData == null ? null : oldData.get(s.id); + // restore previous spoiler/filter revealed states when refreshing + if (oldStatus != null) { + s.spoilerRevealed = oldStatus.spoilerRevealed; + s.filterRevealed = oldStatus.filterRevealed; + } else if (GlobalUserPreferences.autoRevealEqualSpoilers != AutoRevealMode.NEVER && + s.spoilerText != null && + s.spoilerText.equals(mainStatus.spoilerText) && + mainStatus.spoilerRevealed) { + if (GlobalUserPreferences.autoRevealEqualSpoilers == AutoRevealMode.DISCUSSIONS || Objects.equals(mainStatus.account.id, s.account.id)) { + s.spoilerRevealed = true; } } } diff --git a/mastodon/src/main/res/drawable/ic_fluent_star_off_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_star_off_24_regular.xml new file mode 100644 index 000000000..9e46ebd1b --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_star_off_24_regular.xml @@ -0,0 +1,3 @@ + + + diff --git a/mastodon/src/main/res/menu/settings_auto_reveal_spoiler.xml b/mastodon/src/main/res/menu/settings_auto_reveal_spoiler.xml new file mode 100644 index 000000000..f6a5e31ed --- /dev/null +++ b/mastodon/src/main/res/menu/settings_auto_reveal_spoiler.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index 40dd203a8..05d79c5d2 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -297,4 +297,9 @@ Failed loading the profile via %s Load info from remote instances Try fetching more accurate listings for followers, likes and boosts by loading the information from the instance of origin. + Reveal equal CWs in threads + Never + Same author + Discussions + Always \ No newline at end of file