That fuck shit the fascists are using
at master 304 lines 12 kB view raw
1package org.tm.archive.util; 2 3import android.content.Context; 4import android.content.res.Resources; 5import android.graphics.Color; 6import android.graphics.Typeface; 7import android.graphics.drawable.ColorDrawable; 8import android.graphics.drawable.Drawable; 9import android.os.Build; 10import android.text.Layout; 11import android.text.Spannable; 12import android.text.SpannableString; 13import android.text.SpannableStringBuilder; 14import android.text.Spanned; 15import android.text.TextPaint; 16import android.text.TextUtils; 17import android.text.style.AbsoluteSizeSpan; 18import android.text.style.AlignmentSpan; 19import android.text.style.BulletSpan; 20import android.text.style.CharacterStyle; 21import android.text.style.ClickableSpan; 22import android.text.style.DynamicDrawableSpan; 23import android.text.style.ForegroundColorSpan; 24import android.text.style.ImageSpan; 25import android.text.style.RelativeSizeSpan; 26import android.text.style.StyleSpan; 27import android.text.style.TextAppearanceSpan; 28import android.text.style.TypefaceSpan; 29import android.text.style.URLSpan; 30import android.view.View; 31 32import androidx.annotation.ColorInt; 33import androidx.annotation.NonNull; 34import androidx.annotation.StringRes; 35import androidx.annotation.StyleRes; 36import androidx.core.content.ContextCompat; 37 38import org.signal.core.util.DimensionUnit; 39import org.tm.archive.R; 40 41public final class SpanUtil { 42 43 private SpanUtil() {} 44 45 public static final String SPAN_PLACE_HOLDER = "<<<SPAN>>>"; 46 47 private final static Typeface MEDIUM_BOLD_TYPEFACE = Typeface.create("sans-serif-medium", Typeface.BOLD); 48 private final static Typeface BOLD_TYPEFACE = Typeface.create("sans-serif-medium", Typeface.NORMAL); 49 private final static Typeface LIGHT_TYPEFACE = Typeface.create("sans-serif", Typeface.NORMAL); 50 51 public static CharSequence center(@NonNull CharSequence sequence) { 52 SpannableString spannable = new SpannableString(sequence); 53 spannable.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 54 return spannable; 55 } 56 57 public static CharSequence textAppearance(@NonNull Context context, @StyleRes int textAppearance, @NonNull CharSequence sequence) { 58 SpannableString spannable = new SpannableString(sequence); 59 spannable.setSpan(new TextAppearanceSpan(context, textAppearance), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 60 return spannable; 61 } 62 63 public static CharSequence italic(CharSequence sequence) { 64 return italic(sequence, sequence.length()); 65 } 66 67 public static CharSequence italic(CharSequence sequence, int length) { 68 SpannableString spannable = new SpannableString(sequence); 69 spannable.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 70 return spannable; 71 } 72 73 public static CharSequence small(CharSequence sequence) { 74 SpannableString spannable = new SpannableString(sequence); 75 spannable.setSpan(new RelativeSizeSpan(0.9f), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 76 return spannable; 77 } 78 79 public static CharSequence ofSize(CharSequence sequence, int size) { 80 SpannableString spannable = new SpannableString(sequence); 81 spannable.setSpan(new AbsoluteSizeSpan(size, true), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 82 return spannable; 83 } 84 85 public static CharSequence bold(CharSequence sequence) { 86 SpannableString spannable = new SpannableString(sequence); 87 spannable.setSpan(new StyleSpan(Typeface.BOLD), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 88 return spannable; 89 } 90 91 public static CharSequence boldSubstring(CharSequence fullString, CharSequence substring) { 92 SpannableString spannable = new SpannableString(fullString); 93 int start = TextUtils.indexOf(fullString, substring); 94 int end = start + substring.length(); 95 96 if (start >= 0 && end <= fullString.length()) { 97 spannable.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 98 } 99 return spannable; 100 } 101 102 public static CharSequence color(int color, CharSequence sequence) { 103 SpannableString spannable = new SpannableString(sequence); 104 spannable.setSpan(new ForegroundColorSpan(color), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 105 return spannable; 106 } 107 108 public static @NonNull CharSequence bullet(@NonNull CharSequence sequence, int gapWidth) { 109 SpannableString spannable = new SpannableString(sequence); 110 spannable.setSpan(new BulletSpan(gapWidth), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 111 return spannable; 112 } 113 114 public static CharSequence buildImageSpan(@NonNull Drawable drawable) { 115 int flag = Build.VERSION.SDK_INT >= 29 ? DynamicDrawableSpan.ALIGN_CENTER : DynamicDrawableSpan.ALIGN_BASELINE; 116 117 return buildImageSpan(drawable, flag); 118 } 119 120 private static CharSequence buildImageSpan(@NonNull Drawable drawable, int flag) { 121 SpannableString imageSpan = new SpannableString(" "); 122 123 imageSpan.setSpan(new ImageSpan(drawable, flag), 0, imageSpan.length(), 0); 124 125 return imageSpan; 126 } 127 128 public static CharSequence buildCenteredImageSpan(@NonNull Drawable drawable) { 129 SpannableString imageSpan = new SpannableString(" "); 130 imageSpan.setSpan(new CenteredImageSpan(drawable), 0, imageSpan.length(), 0); 131 return imageSpan; 132 } 133 134 public static CharSequence space(int width, @NonNull DimensionUnit widthUnit) { 135 Drawable drawable = new ColorDrawable(Color.TRANSPARENT); 136 drawable.setBounds(0, 0, (int) widthUnit.toPixels(width), 1); 137 138 return buildCenteredImageSpan(drawable); 139 } 140 141 public static void appendCenteredImageSpan(@NonNull SpannableStringBuilder builder, @NonNull Drawable drawable, int width, int height) { 142 drawable.setBounds(0, 0, ViewUtil.dpToPx(width), ViewUtil.dpToPx(height)); 143 builder.append(" ").append(SpanUtil.buildCenteredImageSpan(drawable)); 144 } 145 146 public static void appendSpacer(@NonNull SpannableStringBuilder builder, int width) { 147 SpanUtil.appendCenteredImageSpanWithoutSpace(builder, new ColorDrawable(Color.TRANSPARENT), width, 8); 148 } 149 150 public static void appendCenteredImageSpanWithoutSpace(@NonNull SpannableStringBuilder builder, @NonNull Drawable drawable, int width, int height) { 151 drawable.setBounds(0, 0, ViewUtil.dpToPx(width), ViewUtil.dpToPx(height)); 152 builder.append(SpanUtil.buildCenteredImageSpan(drawable)); 153 } 154 155 public static CharSequence learnMore(@NonNull Context context, 156 @ColorInt int color, 157 @NonNull View.OnClickListener onLearnMoreClicked) 158 { 159 String learnMore = context.getString(R.string.LearnMoreTextView_learn_more); 160 return clickSubstring(learnMore, learnMore, onLearnMoreClicked, color); 161 } 162 163 public static CharSequence readMore(@NonNull Context context, 164 @ColorInt int color, 165 @NonNull View.OnClickListener onLearnMoreClicked) 166 { 167 String readMore = context.getString(R.string.SpanUtil__read_more); 168 return clickSubstring(readMore, readMore, onLearnMoreClicked, color); 169 } 170 171 public static CharSequence clickable(@NonNull CharSequence text, 172 @ColorInt int color, 173 @NonNull View.OnClickListener onLearnMoreClicked) 174 { 175 return clickSubstring(text, text, onLearnMoreClicked, color); 176 } 177 178 public static Spanned urlSubsequence(@NonNull CharSequence fullString, @NonNull CharSequence substring, @NonNull String url) { 179 SpannableString spannable = new SpannableString(fullString); 180 int start = TextUtils.indexOf(fullString, substring); 181 int end = start + substring.length(); 182 183 if (start >= 0 && end <= fullString.length()) { 184 spannable.setSpan(new URLSpan(url), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 185 } 186 return spannable; 187 } 188 189 /** 190 * Takes two resources: 191 * - one resource that has a single string placeholder 192 * - and another resource for a string you want to put in that placeholder with a click listener. 193 * 194 * Example: 195 * 196 * <string name="main_string">This is a %1$s string.</string> 197 * <string name="clickable_string">clickable</string> 198 * 199 * -> This is a clickable string. 200 * (where "clickable" is blue and will trigger the provided click listener when clicked) 201 */ 202 public static Spannable clickSubstring(@NonNull Context context, @StringRes int mainString, @StringRes int clickableString, @NonNull View.OnClickListener clickListener) { 203 String main = context.getString(mainString, SPAN_PLACE_HOLDER); 204 String clickable = context.getString(clickableString); 205 206 int start = main.indexOf(SPAN_PLACE_HOLDER); 207 int end = start + SPAN_PLACE_HOLDER.length(); 208 209 Spannable spannable = new SpannableString(main.substring(0, start) + clickable + main.substring(end)); 210 211 spannable.setSpan(new ClickableSpan() { 212 @Override 213 public void onClick(@NonNull View widget) { 214 clickListener.onClick(widget); 215 } 216 217 @Override 218 public void updateDrawState(@NonNull TextPaint ds) { 219 super.updateDrawState(ds); 220 ds.setUnderlineText(false); 221 } 222 }, start, start + clickable.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 223 224 return spannable; 225 } 226 227 public static CharSequence clickSubstring(@NonNull Context context, 228 @NonNull CharSequence fullString, 229 @NonNull CharSequence substring, 230 @NonNull View.OnClickListener clickListener) { 231 return clickSubstring(fullString, 232 substring, 233 clickListener, 234 ContextCompat.getColor(context, R.color.signal_accent_primary)); 235 } 236 237 public static CharSequence clickSubstring(@NonNull CharSequence fullString, 238 @NonNull CharSequence substring, 239 @NonNull View.OnClickListener clickListener, 240 @ColorInt int linkColor) 241 { 242 ClickableSpan clickable = new ClickableSpan() { 243 @Override 244 public void updateDrawState(@NonNull TextPaint ds) { 245 super.updateDrawState(ds); 246 ds.setUnderlineText(false); 247 ds.setColor(linkColor); 248 } 249 250 @Override 251 public void onClick(@NonNull View widget) { 252 clickListener.onClick(widget); 253 } 254 }; 255 256 SpannableString spannable = new SpannableString(fullString); 257 int start = TextUtils.indexOf(fullString, substring); 258 int end = start + substring.length(); 259 260 if (start >= 0 && end <= fullString.length()) { 261 spannable.setSpan(clickable, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 262 } 263 264 return spannable; 265 } 266 267 public static @NonNull CharSequence insertSingleSpan(@NonNull Resources resources, @StringRes int res, @NonNull CharSequence span) { 268 return replacePlaceHolder(resources.getString(res, SPAN_PLACE_HOLDER), span); 269 } 270 271 public static CharSequence replacePlaceHolder(@NonNull String string, @NonNull CharSequence span) { 272 int index = string.indexOf(SpanUtil.SPAN_PLACE_HOLDER); 273 if (index == -1) { 274 return string; 275 } 276 SpannableStringBuilder builder = new SpannableStringBuilder(string); 277 builder.replace(index, index + SpanUtil.SPAN_PLACE_HOLDER.length(), span); 278 return builder; 279 } 280 281 public static CharacterStyle getMediumBoldSpan() { 282 if (Build.VERSION.SDK_INT >= 28) { 283 return new TypefaceSpan(MEDIUM_BOLD_TYPEFACE); 284 } else { 285 return new StyleSpan(Typeface.BOLD); 286 } 287 } 288 289 public static CharacterStyle getBoldSpan() { 290 if (Build.VERSION.SDK_INT >= 28) { 291 return new TypefaceSpan(BOLD_TYPEFACE); 292 } else { 293 return new StyleSpan(Typeface.BOLD); 294 } 295 } 296 297 public static CharacterStyle getNormalSpan() { 298 if (Build.VERSION.SDK_INT >= 28) { 299 return new TypefaceSpan(LIGHT_TYPEFACE); 300 } else { 301 return new StyleSpan(Typeface.NORMAL); 302 } 303 } 304}