fix: Improve display of "obscured" links (#194)

The previous code (a) used an emoji as the prefix character when showing
the destination of an obscured link, and (b) made the destination part
of the link anchor text.

Using an emoji was a problem because the user can use different emoji
sets and it can give strange results. Making the destination part of the
link text made it difficult to distinguish at a glance where one link
ends and another starts.

Fix the emoji problem by replacing the emoji with a drawable.

Fix the destination problem by changing the string resource so it only
includes the destination part, and inserting it at the end of the link
instead of replacing the whole link. This means the disclosed
destination is not clickable, does not look like part of the link, and
stands out more.
This commit is contained in:
Nik Clayton 2023-10-23 13:20:09 +02:00 committed by GitHub
parent 566b23ed06
commit 5a41cbd40f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 101 additions and 58 deletions

View File

@ -66,6 +66,9 @@ fun CharSequence.emojify(emojis: List<Emoji>?, view: View, animate: Boolean): Ch
class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan() {
var imageDrawable: Drawable? = null
/** Scale the emoji up/down from the calculated size */
var scaleFactor = 1.0f
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
if (fm != null) {
/* update FontMetricsInt or otherwise span does not get drawn when
@ -77,7 +80,7 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
fm.bottom = metrics.bottom
}
return (paint.textSize * 1.2).toInt()
return (paint.textSize * 1.2 * scaleFactor).toInt()
}
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
@ -100,6 +103,9 @@ class EmojiSpan(val viewWeakReference: WeakReference<View>) : ReplacementSpan()
emojiWidth *= drawableSpace / emojiHeight
emojiHeight = drawableSpace
}
emojiHeight *= scaleFactor
emojiWidth *= scaleFactor
drawable.setBounds(0, 0, emojiWidth.toInt(), emojiHeight.toInt())
// vertically center the emoji in the line

View File

@ -44,13 +44,19 @@ import app.pachli.entity.Status.Mention
import app.pachli.interfaces.LinkListener
import app.pachli.settings.PrefKeys
import com.google.android.material.color.MaterialColors
import com.mikepenz.iconics.IconicsColor
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.IconicsSize
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.color
import com.mikepenz.iconics.utils.size
import java.lang.ref.WeakReference
import java.net.URI
import java.net.URISyntaxException
fun getDomain(urlString: String?): String {
val host = urlString?.toUri()?.host
val host = urlString?.toUri()?.host ?: return ""
return when {
host == null -> ""
host.startsWith("www.") -> host.substring(4)
else -> host
}
@ -66,7 +72,7 @@ fun getDomain(urlString: String?): String {
* @param listener to notify about particular spans that are clicked
*/
fun setClickableText(view: TextView, content: CharSequence, mentions: List<Mention>, tags: List<HashTag>?, listener: LinkListener) {
val spannableContent = markupHiddenUrls(view.context, content)
val spannableContent = markupHiddenUrls(view, content)
view.text = spannableContent.apply {
getSpans(0, spannableContent.length, URLSpan::class.java).forEach {
@ -77,7 +83,7 @@ fun setClickableText(view: TextView, content: CharSequence, mentions: List<Menti
}
@VisibleForTesting
fun markupHiddenUrls(context: Context, content: CharSequence): SpannableStringBuilder {
fun markupHiddenUrls(textView: TextView, content: CharSequence): SpannableStringBuilder {
val spannableContent = SpannableStringBuilder(content)
val originalSpans = spannableContent.getSpans(0, content.length, URLSpan::class.java)
val obscuredLinkSpans = originalSpans.filter {
@ -96,12 +102,39 @@ fun markupHiddenUrls(context: Context, content: CharSequence): SpannableStringBu
}
}
for (span in obscuredLinkSpans) {
val start = spannableContent.getSpanStart(span)
val end = spannableContent.getSpanEnd(span)
val originalText = spannableContent.subSequence(start, end)
val replacementText = context.getString(R.string.url_domain_notifier, originalText, getDomain(span.url))
spannableContent.replace(start, end, replacementText) // this also updates the span locations
if (obscuredLinkSpans.isNotEmpty()) {
val context = textView.context
// Drawable to use to mark links. R.string.url_domain_notifier contains a Unicode emoji
// ("🔗") that can render oddly depending on the user's choice of emoji set, so the emoji
// is replaced with the drawable
val iconLinkDrawable = IconicsDrawable(context, GoogleMaterial.Icon.gmd_open_in_new).apply {
size = IconicsSize.px(textView.textSize)
color = IconicsColor.colorInt(textView.currentTextColor)
}
val iconLength = "🔗".length
for (span in obscuredLinkSpans) {
val end = spannableContent.getSpanEnd(span)
val replacementText = context.getString(R.string.url_domain_notifier, getDomain(span.url))
spannableContent.insert(end, replacementText)
}
var iconIndex = -1
while (true) {
iconIndex = spannableContent.indexOf("🔗", iconIndex + 1)
if (iconIndex == -1) break
// ImageSpan has bugs when trying to align the drawable with text, so use
// EmojiSpan which centre-aligns it correctly. EmojiSpan default is to scale
// the drawable to fill the text height, set scaleFactor to get a more
// reasonable size.
val linkSpan = EmojiSpan(WeakReference(textView)).apply {
imageDrawable = iconLinkDrawable
scaleFactor = 0.7f
}
spannableContent.setSpan(linkSpan, iconIndex, iconIndex + iconLength, 0)
}
}
return spannableContent

View File

@ -613,7 +613,7 @@
<string name="description_post_language">لغة المنشور</string>
<string name="delete_scheduled_post_warning">هل تريد حذف هذا المنشور المُبَرمَج؟</string>
<string name="tips_push_notification_migration">أعد تسجيل الدخول إلى جميع الحسابات لتمكين دعم الإشعارات.</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">ضبط نقطة التركيز</string>
<string name="action_edit_image">تعديل الصورة</string>
<string name="action_add_reaction">إضافة رد فعل</string>

View File

@ -596,7 +596,7 @@
<string name="wellbeing_hide_stats_posts">Схаваць колькасную статыстыку допісаў</string>
<string name="wellbeing_hide_stats_profile">Схаваць колькасную статыстыку профіляў</string>
<string name="report_category_violation">Парушэнне правіла</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="instance_rule_title">Правілы %s</string>
<string name="language_display_name_format">%s (%s)</string>
<string name="delete_scheduled_post_warning">Выдаліць гэты запланаваны допіс\?</string>

View File

@ -580,7 +580,7 @@
<string name="action_unfollow_hashtag_format">Deixar de seguir #%s\?</string>
<string name="compose_unsaved_changes">Tens canvis no desats.</string>
<string name="set_focus_description">Toqueu o arrossegueu el cercle per triar el punt focal que sempre serà visible a les miniatures.</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="pref_title_show_self_username">Mostra el nom d\'usuari a les barres d\'eines</string>
<string name="action_set_focus">Estableix el punt d\'enfocament</string>
<string name="description_login">Funciona en la majoria dels casos. No es filtra cap dada a altres aplicacions.</string>

View File

@ -525,7 +525,7 @@
<string name="duration_14_days">14 dní</string>
<string name="duration_30_days">30 dní</string>
<string name="drafts_post_reply_removed">Příspěvek, na který jste připravili odpověď, byl odstraněn</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Nastavit bod zaostření</string>
<string name="tips_push_notification_migration">Znovu se přihlaste ke všem účtům, abyste povolili podporu push oznámení.</string>
<string name="dialog_push_notification_migration">Aby bylo možné používat push oznámení prostřednictvím UnifiedPush, Pachli potřebuje oprávnění k odběru oznámení na vašem serveru Mastodon. To vyžaduje opětovné přihlášení ke změně rozsahů OAuth udělených aplikaci Pachli. Použitím možnosti opětovného přihlášení zde nebo v předvolbách účtu zachováte všechny vaše místní koncepty a mezipaměť.</string>

View File

@ -304,7 +304,7 @@
<string name="filter_expiration_format">%s (%s)</string>
<string name="notification_clear_text">Ydych chi\'n siŵr eich bod chi am glirio\'ch holl hysbysiadau\'n barhaol\?</string>
<string name="error_multimedia_size_limit">Ni all ffeiliau fideo a sain fod yn fwy na %s MB.</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="error_following_hashtag_format">Gwall wrth ddilyn #%s</string>
<string name="error_unfollowing_hashtag_format">Gwall wrth ddad-ddilyn #%s</string>
<string name="action_unmute_domain">Dad-dewi %s</string>

View File

@ -553,7 +553,7 @@
<string name="pref_show_self_username_never">Niemals</string>
<string name="filter_expiration_format">%s (%s)</string>
<string name="description_post_language">Sprache des Beitrags</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Fokuspunkt setzen</string>
<string name="action_add_reaction">Reaktion hinzufügen</string>
<string name="failed_to_remove_from_list">Das Konto konnte nicht aus der Liste entfernt werden</string>

View File

@ -553,5 +553,5 @@
<string name="description_post_language">Mesaĝolingvo</string>
<string name="dialog_push_notification_migration">Por ricevi sciigoj per UnifiedPush, Pachli bezonas taŭgan permeson el Mastodon-servilo. Tio postulas re-ensaluton por ŝanĝi OAuth-rajtoj donitaj al Pachli. Se vi uzas la opcion re-ensaluti ĉi tie aŭ en la agordoj de la konto, viaj malnetoj kaj kaŝmemoroj estos konservitaj.</string>
<string name="dialog_push_notification_migration_other_accounts">Vi re-ensalutis en tiu konto por doni sciigo-permeson al Pachli. Vi havas tamen aliajn kontojn, ĉe kiuj vi devas re-sensaluti. Iru al ili, kaj re-ensalutu por ebligi ricevon de sciigoj per UnifiedPush.</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
</resources>

View File

@ -551,7 +551,7 @@
<string name="failed_to_pin">Fallo al fijar</string>
<string name="failed_to_unpin">Fallo al quitarlo</string>
<string name="pref_show_self_username_disambiguate">Cuando hay varias cuentas ingresadas</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="notification_sign_up_description">Notificaciones de nuevos usuarios</string>
<string name="notification_update_name">Ediciones de una publicación</string>
<string name="notification_update_description">Notificaciones cuando se editan publicaciones con las que has interactuado</string>

View File

@ -542,7 +542,7 @@
<string name="pref_show_self_username_disambiguate">هنگام ورود چندین حساب</string>
<string name="pref_show_self_username_never">هرگز</string>
<string name="pref_title_show_self_username">نمایش نام کاربری در نوارابزارها</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_add_reaction">افزودن واکنش</string>
<string name="action_set_focus">تنظیم نقطهٔ تمرکز</string>
<string name="duration_no_change">(بدون تغییر)</string>

View File

@ -554,7 +554,7 @@
<string name="pref_show_self_username_never">Jamais</string>
<string name="description_post_language">Langue du message</string>
<string name="duration_no_change">(Aucune modification)</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_add_reaction">ajouter une réaction</string>
<string name="instance_rule_title">%s règles</string>
<string name="a11y_label_loading_thread">Chargement du fil</string>

View File

@ -555,7 +555,7 @@
<string name="pref_show_self_username_disambiguate">Nuair a bhios iomadh cunntas air an clàradh a-steach</string>
<string name="pref_show_self_username_never">Chan ann idir</string>
<string name="filter_expiration_format">%s (%s)</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Suidhich puing an fhòcais</string>
<string name="delete_scheduled_post_warning">A bheil thu airson am post sgeidealaichte seo a sguabadh às\?</string>
<string name="instance_rule_info">Le clàradh a-steach, bidh tu ag aontachadh ri riaghailtean %s.</string>

View File

@ -527,7 +527,7 @@
<string name="filter_expiration_format">%s (%s)</string>
<string name="error_multimedia_size_limit">Os ficheiros de vídeo e audio non poden superar os %s MB.</string>
<string name="description_post_language">Idioma de publicación</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Establece foco</string>
<string name="error_following_hashtag_format">Erro ao seguir #%s</string>
<string name="error_unfollowing_hashtag_format">Error ao retirar seguimento de #%s</string>

View File

@ -541,7 +541,7 @@
<string name="pref_show_self_username_disambiguate">Ha több fiók is be van jelentkezve</string>
<string name="pref_show_self_username_never">Soha</string>
<string name="duration_no_change">(Nincs változás)</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Fókuszpont beállítása</string>
<string name="error_following_hashtag_format">Hiba a #%s követésekor</string>
<string name="error_unfollowing_hashtag_format">Hiba a #%s követésének befejezésekor</string>

View File

@ -539,7 +539,7 @@
<string name="failed_to_unpin">Mistókst að losa</string>
<string name="pref_show_self_username_always">Alltaf</string>
<string name="pref_show_self_username_disambiguate">Þegar er skráð inn á mörgum aðgöngum</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Setja virknistað</string>
<string name="pref_show_self_username_never">Aldrei</string>
<string name="pref_title_show_self_username">Birta notandanafn á verkfærastikum</string>

View File

@ -563,7 +563,7 @@
<string name="pref_show_self_username_disambiguate">Quando connesso con più account</string>
<string name="duration_no_change">(nessuna modifica)</string>
<string name="pref_title_show_self_username">Mostra nome utente nelle barre strumenti</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_add_or_remove_from_list">Aggiunti o rimuovi dalla lista</string>
<string name="failed_to_add_to_list">Aggiunta dell\'account alla lista fallita</string>
<string name="failed_to_remove_from_list">Rimozione dell\'account dalla lista fallita</string>

View File

@ -493,7 +493,7 @@
<string name="error_multimedia_size_limit">ビデオと音声ファイルのサイズは %s MB を超えることはできません。</string>
<string name="error_image_edit_failed">画像が編集できませんでした。</string>
<string name="title_login">ログイン</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_edit_image">画像の編集</string>
<string name="instance_rule_title">%s のルール</string>
<string name="error_following_hashtags_unsupported">このインスタンスはハッシュタグのフォローに対応していません。</string>

View File

@ -257,7 +257,7 @@
<string name="pref_title_http_proxy_server">HTTP starpniekserveris</string>
<string name="pref_title_http_proxy_port_message">Portam būtu jābūt starp %d un %d</string>
<string name="action_view_account_preferences">Konta iestatījumi</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="mute_domain_warning">Vai tiešām vēlaties bloķēt visu %s\? Šī domēna saturs netiks rādīts ne publiskajās laika līnijās, ne paziņojumos. Jūsu sekotāji no šī domēna tiks noņemti.</string>
<string name="send_post_notification_saved_content">Ieraksta kopija tika saglabāta tavos melnrakstos</string>
<string name="restart_emoji">Lai pielietotu šīs izmaiņas, ir jāpārstartē Pachli</string>

View File

@ -531,7 +531,7 @@
<string name="filter_expiration_format">%s (%s)</string>
<string name="duration_no_change">(Ingen endring)</string>
<string name="description_post_language">Innleggspråk</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Sett fokuspunkt</string>
<string name="set_focus_description">Trykk eller dra sirkelen for å velge fokuspunktet som alltid skal være synlig i miniatyrbilder.</string>
<string name="pref_show_self_username_always">Alltid</string>

View File

@ -535,7 +535,7 @@
<string name="set_focus_description">Tik of sleep de cirkel naar een centraal focuspunt dat op elke thumbnail zichtbaar moet blijven.</string>
<string name="dialog_push_notification_migration">Om pushmeldingen via UnifiedPush te kunnen gebruiken, moet Pachli zich op meldingen van jouw Mastodon-server abonneren. Dit betekent dat je opnieuw moet inloggen om de OAuth-toestemmingen voor Pachli te wijzigen. Het hier of onder accountvoorkeuren opnieuw inloggen behoudt jouw lokale concepten en buffer.</string>
<string name="dialog_push_notification_migration_other_accounts">Je hebt opnieuw op jouw huidige account ingelogd om toestemming voor pushmeldingen aan Pachli te verlenen. Je hebt echter nog andere accounts die nog niet op deze manier zijn overgezet. Ga naar deze accounts en log één voor één opnieuw in om UnifiedPush-meldingen ook daar in te schakelen.</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="pref_show_self_username_always">Altijd</string>
<string name="pref_show_self_username_disambiguate">Wanneer meerdere accounts zijn ingelogd</string>
<string name="pref_show_self_username_never">Nooit</string>

View File

@ -515,7 +515,7 @@
<string name="pachli_compose_post_quicksetting_label">Compausar una publicacion</string>
<string name="account_date_joined">Arribada del %1$s</string>
<string name="saving_draft">Enregistrament del borrolhon…</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="error_image_edit_failed">Se podiá pas modificar limatge.</string>
<string name="pref_title_wellbeing_mode">Benestar</string>
<string name="duration_no_change">(Cap de modificacion)</string>

View File

@ -560,7 +560,7 @@
<string name="error_multimedia_size_limit">Pliki wideo i audio nie mogą przekraczać rozmiarem %s MB.</string>
<string name="filter_expiration_format">%s (%s)</string>
<string name="description_post_language">Język wpisu</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="duration_no_change">(bez zmian)</string>
<string name="error_following_hashtag_format">Wystąpił błąd podczas obserwowania #%s</string>
<string name="error_unfollowing_hashtag_format">Wystąpił błąd podczas odobserwowywania #%s</string>

View File

@ -560,7 +560,7 @@
<string name="report_category_violation">Violação de regra</string>
<string name="report_category_spam">Spam</string>
<string name="report_category_other">Outros</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="pref_show_self_username_disambiguate">Quando várias contas estão conectadas</string>
<string name="pref_title_http_proxy_port_message">A porta deve estar entre %d e %d</string>
<string name="status_count_one_plus">1+</string>

View File

@ -541,7 +541,7 @@
<string name="set_focus_description">Toca ou arrasta o círculo para escolher o ponto de focagem que estará sempre visível nas pré-visualizações.</string>
<string name="filter_expiration_format">%s(%s)</string>
<string name="duration_no_change">(Sem alteração)</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="pref_show_self_username_always">Sempre</string>
<string name="pref_show_self_username_disambiguate">Quando autenticado em várias contas</string>
<string name="pref_show_self_username_never">Nunca</string>

View File

@ -469,7 +469,7 @@
<string name="duration_365_days">३६५ दिनानि</string>
<string name="duration_no_change">(परिवर्तनं नास्ति)</string>
<string name="review_notifications">सूचनाः सम्दृश्यन्ताम्</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="pref_show_self_username_never">न कदापि</string>
<string name="pref_default_post_language">पूर्वनिविष्टा प्रकाशका भाषा</string>
<string name="notification_report_name">आवेदनानि</string>

View File

@ -263,5 +263,5 @@
<string name="action_links">සබැඳි</string>
<string name="pref_title_browser_settings">අතිරික්සුව</string>
<string name="abbreviated_seconds_ago">තත්. %d</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
</resources>

View File

@ -506,7 +506,7 @@
<string name="dialog_push_notification_migration_other_accounts">Du har loggat in igen på ditt konto för att ge Pachli tillgång till push-prenumeration. Dock har du andra konton som inte har migrerats såhär ännu. Växla till dem och logga in igen för att aktivera stöd för UnifiedPush-notiser.</string>
<string name="action_unsubscribe_account">Sluta prenumerera</string>
<string name="description_post_language">Inläggsspråk</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="pref_show_self_username_disambiguate">När flera konton är inloggade</string>
<string name="pref_show_self_username_never">Aldrig</string>
<string name="notification_sign_up_name">Registreringar</string>

View File

@ -509,7 +509,7 @@
<string name="duration_180_days">180 gün</string>
<string name="duration_60_days">60 gün</string>
<string name="duration_90_days">90 gün</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="pref_default_post_language">Varsayılan gönderme dili</string>
<string name="notification_report_name">Raporlar</string>
<string name="notification_report_description">Denetleme raporlarıyla ilgili bildirimler</string>

View File

@ -550,7 +550,7 @@
<string name="filter_expiration_format">%s (%s)</string>
<string name="description_post_language">Мова допису</string>
<string name="duration_no_change">(Не змінено)</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Налаштувати точку фокусування</string>
<string name="pref_show_self_username_always">Завжди</string>
<string name="set_focus_description">Торкніться або перетягніть коло, щоб вибрати точку фокусування, яку завжди буде видно на мініатюрах.</string>

View File

@ -518,7 +518,7 @@
<string name="filter_expiration_format">%s (%s)</string>
<string name="duration_no_change">(Không đổi)</string>
<string name="description_post_language">Ngôn ngữ đăng</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">Chọn tâm điểm</string>
<string name="set_focus_description">Nhấn hoặc kéo vòng tròn để chọn tiêu điểm sẽ hiển thị trong hình thu nhỏ.</string>
<string name="pref_title_show_self_username">Hiện URL của tôi trên tab</string>

View File

@ -532,7 +532,7 @@
<string name="filter_expiration_format">%s (%s)</string>
<string name="duration_no_change">(无更改)</string>
<string name="description_post_language">嘟文语言</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="action_set_focus">设置焦点</string>
<string name="set_focus_description">轻按或拖动圆圈选择始终在缩略图中可见的焦点。</string>
<string name="pref_show_self_username_disambiguate">登录多个账户时</string>

View File

@ -517,7 +517,7 @@
<string name="filter_expiration_format">%s (%s)</string>
<string name="set_focus_description">輕按或拖動圓圈來選擇總是在縮圖中可視的關注點。</string>
<string name="description_post_language">嘟文語言</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="compose_save_draft_loses_media">是否要儲存草稿?(當你重開草稿時附檔將會被再次上傳。)</string>
<string name="pachli_compose_post_quicksetting_label">編寫嘟文</string>
<string name="failed_to_pin">釘選失敗</string>

View File

@ -753,7 +753,7 @@
<string name="tips_push_notification_migration">Re-login all accounts to enable push notification support.</string>
<string name="dialog_push_notification_migration">In order to use push notifications via UnifiedPush, Pachli needs permission to subscribe to notifications on your Mastodon server. This requires a re-login to change the OAuth scopes granted to Pachli. Using the re-login option here or in "Account preferences" will preserve all of your local drafts and cache.</string>
<string name="dialog_push_notification_migration_other_accounts">You have re-logged into your current account to grant push subscription permission to Pachli. However, you still have other accounts that have not been migrated this way. Switch to them and re-login one by one in order to enable UnifiedPush notifications support.</string>
<string name="url_domain_notifier">%s (🔗 %s)</string>
<string name="url_domain_notifier">\u0020(🔗 %s)</string>
<string name="delete_scheduled_post_warning">Delete this scheduled post?</string>

View File

@ -3,6 +3,7 @@ package app.pachli.util
import android.content.Context
import android.text.SpannableStringBuilder
import android.text.style.URLSpan
import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import app.pachli.R
@ -31,8 +32,11 @@ class LinkHelperTest {
HashTag("mastodev", "https://example.com/Tags/mastodev"),
)
private val textView: TextView
get() = TextView(InstrumentationRegistry.getInstrumentation().targetContext)
private val context: Context
get() = InstrumentationRegistry.getInstrumentation().targetContext
get() = textView.context
@Test
fun whenSettingClickableText_mentionUrlsArePreserved() {
@ -166,8 +170,8 @@ class LinkHelperTest {
content.append(displayedContent, URLSpan(maliciousUrl), 0)
val oldContent = content.toString()
Assert.assertEquals(
context.getString(R.string.url_domain_notifier, displayedContent, maliciousDomain),
markupHiddenUrls(context, content).toString(),
"$displayedContent${context.getString(R.string.url_domain_notifier, maliciousDomain)}",
markupHiddenUrls(textView, content).toString(),
)
Assert.assertEquals(oldContent, content.toString())
}
@ -180,8 +184,8 @@ class LinkHelperTest {
val content = SpannableStringBuilder()
content.append(displayedContent, URLSpan(maliciousUrl), 0)
Assert.assertEquals(
context.getString(R.string.url_domain_notifier, displayedContent, maliciousDomain),
markupHiddenUrls(context, content).toString(),
"$displayedContent${context.getString(R.string.url_domain_notifier, maliciousDomain)}",
markupHiddenUrls(textView, content).toString(),
)
}
@ -194,9 +198,9 @@ class LinkHelperTest {
content.append(displayedContent, URLSpan("https://$domain/foo/bar"), 0)
}
val markedUpContent = markupHiddenUrls(context, content)
val markedUpContent = markupHiddenUrls(textView, content)
for (domain in domains) {
Assert.assertTrue(markedUpContent.contains(context.getString(R.string.url_domain_notifier, displayedContent, domain)))
Assert.assertTrue(markedUpContent.contains(context.getString(R.string.url_domain_notifier, domain)))
}
}
@ -213,7 +217,7 @@ class LinkHelperTest {
.append("$domain/", URLSpan("https://$domain"), 0)
.append("$domain/", URLSpan("https://www.$domain"), 0)
val markedUpContent = markupHiddenUrls(context, content)
val markedUpContent = markupHiddenUrls(textView, content)
Assert.assertFalse(markedUpContent.contains("🔗"))
}
@ -226,7 +230,7 @@ class LinkHelperTest {
.append("Some Place | https://some.place/", URLSpan("https://some.place/"), 0)
.append("Some Place https://some.place/path", URLSpan("https://some.place/path"), 0)
val markedUpContent = markupHiddenUrls(context, content)
val markedUpContent = markupHiddenUrls(textView, content)
Assert.assertFalse(markedUpContent.contains("🔗"))
}
@ -239,7 +243,7 @@ class LinkHelperTest {
.append("Another Place | https://another.place/", URLSpan("https://some.place/"), 0)
.append("Another Place https://another.place/path", URLSpan("https://some.place/path"), 0)
val markedUpContent = markupHiddenUrls(context, content)
val markedUpContent = markupHiddenUrls(textView, content)
val asserts = listOf(
"Another Place: another.place",
"Another Place: another.place/",
@ -248,7 +252,7 @@ class LinkHelperTest {
"Another Place https://another.place/path",
)
asserts.forEach {
Assert.assertTrue(markedUpContent.contains(context.getString(R.string.url_domain_notifier, it, "some.place")))
Assert.assertTrue(markedUpContent.contains(context.getString(R.string.url_domain_notifier, "some.place")))
}
}
@ -260,7 +264,7 @@ class LinkHelperTest {
builder.append(" ")
}
val markedUpContent = markupHiddenUrls(context, builder)
val markedUpContent = markupHiddenUrls(textView, builder)
for (mention in mentions) {
Assert.assertFalse(markedUpContent.contains("${getDomain(mention.url)})"))
}
@ -274,7 +278,7 @@ class LinkHelperTest {
builder.append(" ")
}
val markedUpContent = markupHiddenUrls(context, builder)
val markedUpContent = markupHiddenUrls(textView, builder)
for (mention in mentions) {
Assert.assertFalse(markedUpContent.contains("${getDomain(mention.url)})"))
}
@ -288,7 +292,7 @@ class LinkHelperTest {
builder.append(" ")
}
val markedUpContent = markupHiddenUrls(context, builder)
val markedUpContent = markupHiddenUrls(textView, builder)
for (tag in tags) {
Assert.assertFalse(markedUpContent.contains("${getDomain(tag.url)})"))
}
@ -302,7 +306,7 @@ class LinkHelperTest {
builder.append(" ")
}
val markedUpContent = markupHiddenUrls(context, builder)
val markedUpContent = markupHiddenUrls(textView, builder)
for (tag in tags) {
Assert.assertFalse(markedUpContent.contains("${getDomain(tag.url)})"))
}