Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Machine translation of posts #4307

Merged
merged 11 commits into from
Mar 9, 2024
1,040 changes: 1,040 additions & 0 deletions app/schemas/com.keylesspalace.tusky.db.AppDatabase/58.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion app/src/main/java/com/keylesspalace/tusky/AboutActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class AboutActivity : BottomSheetActivity(), Injectable {

lifecycleScope.launch {
accountManager.activeAccount?.let { account ->
val instanceInfo = instanceInfoRepository.getInstanceInfo()
val instanceInfo = instanceInfoRepository.getUpdatedInstanceInfoOrFallback()
binding.accountInfo.text = getString(
R.string.about_account_info,
account.username,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.keylesspalace.tusky.entity.FilterResult;
import com.keylesspalace.tusky.entity.HashTag;
import com.keylesspalace.tusky.entity.Status;
import com.keylesspalace.tusky.entity.Translation;
import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.util.AbsoluteTimeFormatter;
import com.keylesspalace.tusky.util.AttachmentHelper;
Expand All @@ -56,6 +57,7 @@
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.LocaleUtilsKt;
import com.keylesspalace.tusky.util.NumberUtils;
import com.keylesspalace.tusky.util.StatusDisplayOptions;
import com.keylesspalace.tusky.util.TimestampUtils;
Expand All @@ -66,11 +68,13 @@
import com.keylesspalace.tusky.viewdata.PollViewData;
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
import com.keylesspalace.tusky.viewdata.StatusViewData;
import com.keylesspalace.tusky.viewdata.TranslationViewData;

import java.text.NumberFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import at.connyduck.sparkbutton.SparkButton;
import at.connyduck.sparkbutton.helpers.Utils;
Expand Down Expand Up @@ -120,6 +124,9 @@ public static class Key {
protected final TextView filteredPlaceholderLabel;
protected final Button filteredPlaceholderShowButton;
protected final ConstraintLayout statusContainer;
private final TextView translationStatusView;
private final Button untranslateButton;


private final NumberFormat numberFormat = NumberFormat.getNumberInstance();
private final AbsoluteTimeFormatter absoluteTimeFormatter = new AbsoluteTimeFormatter();
Expand Down Expand Up @@ -182,6 +189,9 @@ protected StatusBaseViewHolder(@NonNull View itemView) {
pollOptions.setLayoutManager(new LinearLayoutManager(pollOptions.getContext()));
((DefaultItemAnimator) pollOptions.getItemAnimator()).setSupportsChangeAnimations(false);

translationStatusView = itemView.findViewById(R.id.status_translation_status);
untranslateButton = itemView.findViewById(R.id.status_button_untranslate);

this.avatarRadius48dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_48dp);
this.avatarRadius36dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_36dp);
this.avatarRadius24dp = itemView.getContext().getResources().getDimensionPixelSize(R.dimen.avatar_radius_24dp);
Expand Down Expand Up @@ -213,7 +223,7 @@ protected void setSpoilerAndContent(@NonNull StatusViewData.Concrete status,
final @NonNull StatusActionListener listener) {

Status actionable = status.getActionable();
String spoilerText = actionable.getSpoilerText();
String spoilerText = status.getSpoilerText();
List<Emoji> emojis = actionable.getEmojis();

boolean sensitive = !TextUtils.isEmpty(spoilerText);
Expand Down Expand Up @@ -273,7 +283,7 @@ private void setTextVisible(boolean sensitive,
List<Status.Mention> mentions = actionable.getMentions();
List<HashTag> tags = actionable.getTags();
List<Emoji> emojis = actionable.getEmojis();
PollViewData poll = PollViewDataKt.toViewData(actionable.getPoll());
PollViewData poll = PollViewDataKt.toViewData(status.getPoll());

if (expanded) {
CharSequence emojifiedText = CustomEmojiHelper.emojify(content, emojis, this.content, statusDisplayOptions.animateEmojis());
Expand Down Expand Up @@ -779,7 +789,7 @@ public void setupWithStatus(@NonNull StatusViewData.Concrete status,
setReblogged(actionable.getReblogged());
setFavourited(actionable.getFavourited());
setBookmarked(actionable.getBookmarked());
List<Attachment> attachments = actionable.getAttachments();
List<Attachment> attachments = status.getAttachments();
boolean sensitive = actionable.getSensitive();
if (statusDisplayOptions.mediaPreviewEnabled() && hasPreviewableAttachment(attachments)) {
setMediaPreviews(attachments, sensitive, listener, status.isShowingContent(), statusDisplayOptions.useBlurhash());
Expand All @@ -802,6 +812,9 @@ public void setupWithStatus(@NonNull StatusViewData.Concrete status,

setupButtons(listener, actionable.getAccount().getId(), status.getContent().toString(),
statusDisplayOptions);

setTranslationStatus(status, listener);

setRebloggingEnabled(actionable.rebloggingAllowed(), actionable.getVisibility());

setSpoilerAndContent(status, statusDisplayOptions, listener);
Expand All @@ -827,6 +840,29 @@ public void setupWithStatus(@NonNull StatusViewData.Concrete status,
}
}

private void setTranslationStatus(StatusViewData.Concrete status, StatusActionListener listener) {
var translationViewData = status.getTranslation();
if (translationViewData != null) {
if (translationViewData instanceof TranslationViewData.Loaded) {
Translation translation = ((TranslationViewData.Loaded) translationViewData).getData();
translationStatusView.setVisibility(View.VISIBLE);
var langName = LocaleUtilsKt.localeNameForUntrustedISO639LangCode(translation.getDetectedSourceLanguage());
translationStatusView.setText(translationStatusView.getContext().getString(R.string.label_translated, langName, translation.getProvider()));
untranslateButton.setVisibility(View.VISIBLE);
untranslateButton.setOnClickListener((v) -> listener.onUntranslate(getBindingAdapterPosition()));
} else {
translationStatusView.setVisibility(View.VISIBLE);
translationStatusView.setText(R.string.label_translating);
untranslateButton.setVisibility(View.GONE);
untranslateButton.setOnClickListener(null);
}
} else {
translationStatusView.setVisibility(View.GONE);
untranslateButton.setVisibility(View.GONE);
untranslateButton.setOnClickListener(null);
}
}

private void setupFilterPlaceholder(StatusViewData.Concrete status, StatusActionListener listener, StatusDisplayOptions displayOptions) {
if (status.getFilterAction() != Filter.Action.WARN) {
showFilteredPlaceholder(false);
Expand Down Expand Up @@ -864,27 +900,57 @@ private void setDescriptionForStatus(@NonNull StatusViewData.Concrete status,
Status actionable = status.getActionable();

String description = context.getString(R.string.description_status,
// 1 display_name
actionable.getAccount().getDisplayName(),
// 2 CW?
getContentWarningDescription(context, status),
(TextUtils.isEmpty(actionable.getSpoilerText()) || !actionable.getSensitive() || status.isExpanded() ? status.getContent() : ""),
// 3 content?
(TextUtils.isEmpty(status.getSpoilerText()) || !actionable.getSensitive() || status.isExpanded() ? status.getContent() : ""),
// 4 date
getCreatedAtDescription(actionable.getCreatedAt(), statusDisplayOptions),
// 5 edited?
actionable.getEditedAt() != null ? context.getString(R.string.description_post_edited) : "",
// 6 reposted_by?
getReblogDescription(context, status),
// 7 username
actionable.getAccount().getUsername(),
// 8 reposted
actionable.getReblogged() ? context.getString(R.string.description_post_reblogged) : "",
// 9 favorited
actionable.getFavourited() ? context.getString(R.string.description_post_favourited) : "",
// 10 bookmarked
actionable.getBookmarked() ? context.getString(R.string.description_post_bookmarked) : "",
// 11 media
getMediaDescription(context, status),
// 12 visibility
getVisibilityDescription(context, actionable.getVisibility()),
// 13 fav_number
getFavsText(context, actionable.getFavouritesCount()),
// 14 reblog_number
getReblogsText(context, actionable.getReblogsCount()),
getPollDescription(status, context, statusDisplayOptions)
// 15 poll?
getPollDescription(status, context, statusDisplayOptions),
// 16 translated?
getTranslatedDescription(context, status.getTranslation())
);
itemView.setContentDescription(description);
}

private String getTranslatedDescription(Context context, TranslationViewData translationViewData) {
if (translationViewData == null) {
return "";
} else if (translationViewData instanceof TranslationViewData.Loading) {
return context.getString(R.string.label_translating);
} else {
Translation translation = ((TranslationViewData.Loaded) translationViewData).getData();
var langName = LocaleUtilsKt.localeNameForUntrustedISO639LangCode(translation.getDetectedSourceLanguage());
return context.getString(R.string.label_translated, langName, translation.getProvider());
}
}

private static CharSequence getReblogDescription(Context context,
@NonNull StatusViewData.Concrete status) {
@Nullable
Status reblog = status.getRebloggingStatus();
if (reblog != null) {
return context
Expand All @@ -895,12 +961,12 @@ private static CharSequence getReblogDescription(Context context,
}

private static CharSequence getMediaDescription(Context context,
@NonNull StatusViewData.Concrete status) {
if (status.getActionable().getAttachments().isEmpty()) {
@NonNull StatusViewData.Concrete viewData) {
if (viewData.getAttachments().isEmpty()) {
return "";
}
StringBuilder mediaDescriptions = CollectionsKt.fold(
status.getActionable().getAttachments(),
viewData.getAttachments(),
new StringBuilder(),
(builder, a) -> {
if (a.getDescription() == null) {
Expand All @@ -917,8 +983,8 @@ private static CharSequence getMediaDescription(Context context,

private static CharSequence getContentWarningDescription(Context context,
@NonNull StatusViewData.Concrete status) {
if (!TextUtils.isEmpty(status.getActionable().getSpoilerText())) {
return context.getString(R.string.description_post_cw, status.getActionable().getSpoilerText());
if (!TextUtils.isEmpty(status.getSpoilerText())) {
return context.getString(R.string.description_post_cw, status.getSpoilerText());
} else {
return "";
}
Expand Down Expand Up @@ -954,7 +1020,7 @@ protected static CharSequence getVisibilityDescription(@NonNull Context context,
private CharSequence getPollDescription(@NonNull StatusViewData.Concrete status,
Context context,
StatusDisplayOptions statusDisplayOptions) {
PollViewData poll = PollViewDataKt.toViewData(status.getActionable().getPoll());
PollViewData poll = PollViewDataKt.toViewData(status.getPoll());
if (poll == null) {
return "";
} else {
Expand All @@ -981,7 +1047,7 @@ protected CharSequence getFavsText(@NonNull Context context, int count) {
}

@NonNull
protected CharSequence getReblogsText (@NonNull Context context, int count) {
protected CharSequence getReblogsText(@NonNull Context context, int count) {
String countString = numberFormat.format(count);
return HtmlCompat.fromHtml(context.getResources().getQuantityString(R.plurals.reblogs, count, countString), HtmlCompat.FROM_HTML_MODE_LEGACY);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import android.text.style.DynamicDrawableSpan;
import android.text.style.ImageSpan;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.appcompat.widget.ViewUtils;
import androidx.recyclerview.widget.RecyclerView;

import com.keylesspalace.tusky.R;
Expand All @@ -23,6 +25,7 @@
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.NoUnderlineURLSpan;
import com.keylesspalace.tusky.util.StatusDisplayOptions;
import com.keylesspalace.tusky.util.ViewExtensionsKt;
import com.keylesspalace.tusky.viewdata.StatusViewData;

import java.text.DateFormat;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class ComposeViewModel @Inject constructor(
private var currentContent: String? = ""
private var currentContentWarning: String? = ""

val instanceInfo: SharedFlow<InstanceInfo> = instanceInfoRepo::getInstanceInfo.asFlow()
val instanceInfo: SharedFlow<InstanceInfo> = instanceInfoRepo::getUpdatedInstanceInfoOrFallback.asFlow()
.shareIn(viewModelScope, SharingStarted.Eagerly, replay = 1)

val emoji: SharedFlow<List<Emoji>> = instanceInfoRepo::getEmojis.asFlow()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,11 @@ class ConversationsFragment :

if (loadState.isAnyLoading()) {
lifecycleScope.launch {
eventHub.dispatch(ConversationsLoadingEvent(accountManager.activeAccount?.accountId ?: ""))
eventHub.dispatch(
ConversationsLoadingEvent(
accountManager.activeAccount?.accountId ?: ""
)
)
}
}

Expand All @@ -153,12 +157,14 @@ class ConversationsFragment :
binding.statusView.showHelp(R.string.help_empty_conversations)
}
}

is LoadState.Error -> {
binding.statusView.show()
binding.statusView.setup(
(loadState.refresh as LoadState.Error).error
) { refreshContent() }
}

is LoadState.Loading -> {
binding.progressBar.show()
}
Expand Down Expand Up @@ -242,6 +248,7 @@ class ConversationsFragment :
refreshContent()
true
}

else -> false
}
}
Expand All @@ -256,7 +263,8 @@ class ConversationsFragment :

(binding.recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false

binding.recyclerView.adapter = adapter.withLoadStateFooter(ConversationLoadStateAdapter(adapter::retry))
binding.recyclerView.adapter =
adapter.withLoadStateFooter(ConversationLoadStateAdapter(adapter::retry))
}

private fun refreshContent() {
Expand Down Expand Up @@ -284,6 +292,8 @@ class ConversationsFragment :
}
}

override val onMoreTranslate: ((translate: Boolean, position: Int) -> Unit)? = null

override fun onMore(view: View, position: Int) {
adapter.peek(position)?.let { conversation ->

Expand Down Expand Up @@ -386,6 +396,10 @@ class ConversationsFragment :
}
}

override fun onUntranslate(position: Int) {
// not needed
}

private fun deleteConversation(conversation: ConversationViewData) {
AlertDialog.Builder(requireContext())
.setMessage(R.string.dialog_delete_conversation_warning)
Expand All @@ -402,6 +416,7 @@ class ConversationsFragment :
PrefKeys.FAB_HIDE -> {
hideFab = sharedPreferences.getBoolean(PrefKeys.FAB_HIDE, false)
}

PrefKeys.MEDIA_PREVIEW_ENABLED -> {
val enabled = accountManager.activeAccount!!.mediaPreviewEnabled
val oldMediaPreviewEnabled = adapter.mediaPreviewEnabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ data class InstanceInfo(
val maxFields: Int,
val maxFieldNameLength: Int?,
val maxFieldValueLength: Int?,
val version: String?
val version: String?,
val translationEnabled: Boolean?,
)
Loading