diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/AkkomaTranslateStatus.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/AkkomaTranslateStatus.java new file mode 100644 index 0000000000..f47f7c2c1b --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/AkkomaTranslateStatus.java @@ -0,0 +1,11 @@ +package org.joinmastodon.android.api.requests.statuses; + +import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.model.AkkomaTranslation; + +public class AkkomaTranslateStatus extends MastodonAPIRequest{ + public AkkomaTranslateStatus(String id, String lang){ + super(HttpMethod.GET, "/statuses/"+id+"/translations/"+lang.toUpperCase(), AkkomaTranslation.class); + } +} + 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 d4c1a870d4..390033bf35 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -19,12 +19,15 @@ import org.joinmastodon.android.E; import org.joinmastodon.android.GlobalUserPreferences; import org.joinmastodon.android.R; +import org.joinmastodon.android.api.MastodonAPIRequest; import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships; import org.joinmastodon.android.api.requests.polls.SubmitPollVote; +import org.joinmastodon.android.api.requests.statuses.AkkomaTranslateStatus; import org.joinmastodon.android.api.requests.statuses.TranslateStatus; import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.events.PollUpdatedEvent; import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.AkkomaTranslation; import org.joinmastodon.android.model.DisplayItemsParent; import org.joinmastodon.android.model.Poll; import org.joinmastodon.android.model.Relationship; @@ -60,6 +63,7 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; import androidx.annotation.NonNull; @@ -855,38 +859,52 @@ public void togglePostTranslation(Status status, String itemID){ status.translationState=Status.TranslationState.SHOWN; }else{ status.translationState=Status.TranslationState.LOADING; - new TranslateStatus(status.getContentStatus().id, Locale.getDefault().getLanguage()) - .setCallback(new Callback<>(){ + Consumer successCallback=(result)->{ + status.translation=result; + status.translationState=Status.TranslationState.SHOWN; + updateTranslation(itemID); + }; + MastodonAPIRequest req=isInstanceAkkoma() + ? new AkkomaTranslateStatus(status.getContentStatus().id, Locale.getDefault().getLanguage()).setCallback(new Callback<>(){ + @Override + public void onSuccess(AkkomaTranslation result){ + if(getActivity()!=null) successCallback.accept(result.toTranslation()); + } + @Override + public void onError(ErrorResponse error){ + if(getActivity()!=null) translationCallbackError(status, itemID); + } + }) + : new TranslateStatus(status.getContentStatus().id, Locale.getDefault().getLanguage()).setCallback(new Callback<>(){ @Override public void onSuccess(Translation result){ - if(getActivity()==null) - return; - status.translation=result; - status.translationState=Status.TranslationState.SHOWN; - updateTranslation(itemID); + if(getActivity()!=null) successCallback.accept(result); } @Override public void onError(ErrorResponse error){ - if(getActivity()==null) - return; - status.translationState=Status.TranslationState.HIDDEN; - updateTranslation(itemID); - new M3AlertDialogBuilder(getActivity()) - .setTitle(R.string.error) - .setMessage(R.string.translation_failed) - .setPositiveButton(R.string.ok, null) - .show(); + if(getActivity()!=null) translationCallbackError(status, itemID); } - }) - .setTimeout(60000) // 1 minute - .exec(accountID); + }); + + // 1 minute + req.setTimeout(60000).exec(accountID); } } } updateTranslation(itemID); } + private void translationCallbackError(Status status, String itemID) { + status.translationState=Status.TranslationState.HIDDEN; + updateTranslation(itemID); + new M3AlertDialogBuilder(getActivity()) + .setTitle(R.string.error) + .setMessage(R.string.translation_failed) + .setPositiveButton(R.string.ok, null) + .show(); + } + private void updateTranslation(String itemID) { TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class); if(text!=null){ @@ -896,6 +914,9 @@ private void updateTranslation(String itemID) { notifyItemChanged(itemID, TextStatusDisplayItem.class); } + if(isInstanceAkkoma()) + return; + SpoilerStatusDisplayItem.Holder spoiler=findHolderOfType(itemID, SpoilerStatusDisplayItem.Holder.class); if(spoiler!=null){ spoiler.rebind(); diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/AkkomaTranslation.java b/mastodon/src/main/java/org/joinmastodon/android/model/AkkomaTranslation.java new file mode 100644 index 0000000000..fd024a654a --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/model/AkkomaTranslation.java @@ -0,0 +1,14 @@ +package org.joinmastodon.android.model; + +public class AkkomaTranslation extends BaseModel{ + public String text; + public String detectedLanguage; + + public Translation toTranslation() { + Translation translation=new Translation(); + translation.content=text; + translation.detectedSourceLanguage=detectedLanguage; + translation.provider="Akkoma"; + return translation; + } +} 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 f90dfd4bde..aacf41b9a9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Instance.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Instance.java @@ -161,11 +161,15 @@ public boolean hasFeature(Feature feature) { case BUBBLE_TIMELINE -> pleromaFeatures .map(f -> f.contains("bubble_timeline")) .orElse(false); + case MACHINE_TRANSLATION -> pleromaFeatures + .map(f -> f.contains("akkoma:machine_translation")) + .orElse(false); }; } public enum Feature { - BUBBLE_TIMELINE + BUBBLE_TIMELINE, + MACHINE_TRANSLATION } @Parcel 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 6498cb0f74..01b2ce29c1 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -220,10 +220,11 @@ public Status clone(){ public static final Pattern BOTTOM_TEXT_PATTERN = Pattern.compile("(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️)(?:\uD83D\uDC49\uD83D\uDC48(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️))*\uD83D\uDC49\uD83D\uDC48"); public boolean isEligibleForTranslation(AccountSession session){ - Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(session.domain); - boolean translateEnabled = instanceInfo != null && - instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null && - instanceInfo.v2.configuration.translation.enabled; + Instance instanceInfo=AccountSessionManager.getInstance().getInstanceInfo(session.domain); + boolean translateEnabled=instanceInfo!=null && ( + (instanceInfo.v2!=null && instanceInfo.v2.configuration.translation!=null && instanceInfo.v2.configuration.translation.enabled) || + (instanceInfo.isAkkoma() && instanceInfo.hasFeature(Instance.Feature.MACHINE_TRANSLATION)) + ); try { Pair> decoded=BOTTOM_TEXT_PATTERN.matcher(getStrippedText()).find() 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 9235e6d99b..ecbf2f3d5e 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 @@ -191,7 +191,7 @@ private CustomEmojiHelper getEmojiHelper(){ public void updateTranslation(boolean updateText){ if(item.status==null) return; - boolean translateEnabled=!item.disableTranslate && item.status.isEligibleForTranslation(item.parentFragment.getSession()); + boolean translateEnabled=!item.disableTranslate && item.status.isEligibleForTranslation(item.parentFragment.getSession()) && !item.isForQuote; if(translationFooter==null && translateEnabled){ translationFooter=translationFooterStub.inflate(); translationInfo=findViewById(R.id.translation_info_text);