That fuck shit the fascists are using
at master 485 lines 15 kB view raw
1/* 2 * Copyright (C) 2011 Whisper Systems 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17package org.tm.archive.util; 18 19import android.annotation.SuppressLint; 20import android.app.ActivityManager; 21import android.content.ClipData; 22import android.content.ClipboardManager; 23import android.content.Context; 24import android.content.pm.PackageManager; 25import android.graphics.Typeface; 26import android.net.Uri; 27import android.telephony.TelephonyManager; 28import android.text.Spannable; 29import android.text.SpannableString; 30import android.text.TextUtils; 31import android.text.style.StyleSpan; 32 33import androidx.annotation.NonNull; 34import androidx.annotation.Nullable; 35import androidx.annotation.RequiresPermission; 36 37import com.annimon.stream.Stream; 38import com.google.i18n.phonenumbers.NumberParseException; 39import com.google.i18n.phonenumbers.PhoneNumberUtil; 40import com.google.i18n.phonenumbers.Phonenumber; 41 42import org.signal.core.util.Base64; 43import org.signal.core.util.logging.Log; 44import org.tm.archive.BuildConfig; 45import org.tm.archive.R; 46import org.tm.archive.components.ComposeText; 47import org.tm.archive.keyvalue.SignalStore; 48 49import java.io.ByteArrayOutputStream; 50import java.io.IOException; 51import java.nio.charset.StandardCharsets; 52import java.security.SecureRandom; 53import java.util.ArrayList; 54import java.util.Arrays; 55import java.util.Collection; 56import java.util.Collections; 57import java.util.LinkedList; 58import java.util.List; 59import java.util.Map; 60import java.util.Optional; 61import java.util.concurrent.TimeUnit; 62 63public class Util { 64 private static final String TAG = Log.tag(Util.class); 65 66 private static final long BUILD_LIFESPAN = TimeUnit.DAYS.toMillis(90); 67 68 public static final String COPY_LABEL = "text\u00AD"; 69 70 public static <T> List<T> asList(T... elements) { 71 List<T> result = new LinkedList<>(); 72 Collections.addAll(result, elements); 73 return result; 74 } 75 76 public static String join(String[] list, String delimiter) { 77 return join(Arrays.asList(list), delimiter); 78 } 79 80 public static <T> String join(Collection<T> list, String delimiter) { 81 StringBuilder result = new StringBuilder(); 82 int i = 0; 83 84 for (T item : list) { 85 result.append(item); 86 87 if (++i < list.size()) 88 result.append(delimiter); 89 } 90 91 return result.toString(); 92 } 93 94 public static String join(long[] list, String delimeter) { 95 List<Long> boxed = new ArrayList<>(list.length); 96 97 for (int i = 0; i < list.length; i++) { 98 boxed.add(list[i]); 99 } 100 101 return join(boxed, delimeter); 102 } 103 104 @SafeVarargs 105 public static @NonNull <E> List<E> join(@NonNull List<E>... lists) { 106 int totalSize = Stream.of(lists).reduce(0, (sum, list) -> sum + list.size()); 107 List<E> joined = new ArrayList<>(totalSize); 108 109 for (List<E> list : lists) { 110 joined.addAll(list); 111 } 112 113 return joined; 114 } 115 116 public static String join(List<Long> list, String delimeter) { 117 StringBuilder sb = new StringBuilder(); 118 119 for (int j = 0; j < list.size(); j++) { 120 if (j != 0) sb.append(delimeter); 121 sb.append(list.get(j)); 122 } 123 124 return sb.toString(); 125 } 126 127 public static String rightPad(String value, int length) { 128 if (value.length() >= length) { 129 return value; 130 } 131 132 StringBuilder out = new StringBuilder(value); 133 while (out.length() < length) { 134 out.append(" "); 135 } 136 137 return out.toString(); 138 } 139 140 public static boolean isEmpty(ComposeText value) { 141 return value == null || value.getText() == null || TextUtils.isEmpty(value.getTextTrimmed()); 142 } 143 144 public static boolean isEmpty(Collection<?> collection) { 145 return collection == null || collection.isEmpty(); 146 } 147 148 public static boolean isEmpty(@Nullable CharSequence charSequence) { 149 return charSequence == null || charSequence.length() == 0; 150 } 151 152 public static boolean hasItems(@Nullable Collection<?> collection) { 153 return collection != null && !collection.isEmpty(); 154 } 155 156 public static <K, V> boolean hasItems(@Nullable Map<K, V> map) { 157 return map != null && !map.isEmpty(); 158 } 159 160 public static <K, V> V getOrDefault(@NonNull Map<K, V> map, K key, V defaultValue) { 161 return map.containsKey(key) ? map.get(key) : defaultValue; 162 } 163 164 public static String getFirstNonEmpty(String... values) { 165 for (String value : values) { 166 if (!Util.isEmpty(value)) { 167 return value; 168 } 169 } 170 return ""; 171 } 172 173 public static @NonNull String emptyIfNull(@Nullable String value) { 174 return value != null ? value : ""; 175 } 176 177 public static @NonNull CharSequence emptyIfNull(@Nullable CharSequence value) { 178 return value != null ? value : ""; 179 } 180 181 public static CharSequence getBoldedString(String value) { 182 SpannableString spanned = new SpannableString(value); 183 spanned.setSpan(new StyleSpan(Typeface.BOLD), 0, 184 spanned.length(), 185 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 186 187 return spanned; 188 } 189 190 public static @NonNull String toIsoString(byte[] bytes) { 191 return new String(bytes, StandardCharsets.ISO_8859_1); 192 } 193 194 public static byte[] toIsoBytes(String isoString) { 195 return isoString.getBytes(StandardCharsets.ISO_8859_1); 196 } 197 198 public static byte[] toUtf8Bytes(String utf8String) { 199 return utf8String.getBytes(StandardCharsets.UTF_8); 200 } 201 202 public static void wait(Object lock, long timeout) { 203 try { 204 lock.wait(timeout); 205 } catch (InterruptedException ie) { 206 throw new AssertionError(ie); 207 } 208 } 209 210 @RequiresPermission(anyOf = { 211 android.Manifest.permission.READ_PHONE_STATE, 212 android.Manifest.permission.READ_PHONE_NUMBERS 213 }) 214 @SuppressLint("MissingPermission") 215 public static Optional<Phonenumber.PhoneNumber> getDeviceNumber(Context context) { 216 try { 217 final String localNumber = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number(); 218 final Optional<String> countryIso = getSimCountryIso(context); 219 220 if (TextUtils.isEmpty(localNumber)) return Optional.empty(); 221 if (!countryIso.isPresent()) return Optional.empty(); 222 223 return Optional.ofNullable(PhoneNumberUtil.getInstance().parse(localNumber, countryIso.get())); 224 } catch (NumberParseException e) { 225 Log.w(TAG, e); 226 return Optional.empty(); 227 } 228 } 229 230 public static Optional<String> getSimCountryIso(Context context) { 231 String simCountryIso = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getSimCountryIso(); 232 return Optional.ofNullable(simCountryIso != null ? simCountryIso.toUpperCase() : null); 233 } 234 235 public static @NonNull <T> T firstNonNull(@Nullable T optional, @NonNull T fallback) { 236 return optional != null ? optional : fallback; 237 } 238 239 @SafeVarargs 240 public static @NonNull <T> T firstNonNull(T ... ts) { 241 for (T t : ts) { 242 if (t != null) { 243 return t; 244 } 245 } 246 247 throw new IllegalStateException("All choices were null."); 248 } 249 250 public static <T> List<List<T>> partition(List<T> list, int partitionSize) { 251 List<List<T>> results = new LinkedList<>(); 252 253 for (int index=0;index<list.size();index+=partitionSize) { 254 int subListSize = Math.min(partitionSize, list.size() - index); 255 256 results.add(list.subList(index, index + subListSize)); 257 } 258 259 return results; 260 } 261 262 public static List<String> split(String source, String delimiter) { 263 List<String> results = new LinkedList<>(); 264 265 if (TextUtils.isEmpty(source)) { 266 return results; 267 } 268 269 String[] elements = source.split(delimiter); 270 Collections.addAll(results, elements); 271 272 return results; 273 } 274 275 public static byte[][] split(byte[] input, int firstLength, int secondLength) { 276 byte[][] parts = new byte[2][]; 277 278 parts[0] = new byte[firstLength]; 279 System.arraycopy(input, 0, parts[0], 0, firstLength); 280 281 parts[1] = new byte[secondLength]; 282 System.arraycopy(input, firstLength, parts[1], 0, secondLength); 283 284 return parts; 285 } 286 287 public static byte[] combine(byte[]... elements) { 288 try { 289 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 290 291 for (byte[] element : elements) { 292 baos.write(element); 293 } 294 295 return baos.toByteArray(); 296 } catch (IOException e) { 297 throw new AssertionError(e); 298 } 299 } 300 301 public static byte[] trim(byte[] input, int length) { 302 byte[] result = new byte[length]; 303 System.arraycopy(input, 0, result, 0, result.length); 304 305 return result; 306 } 307 308 /** 309 * The app version. 310 * <p> 311 * This code should be used in all places that compare app versions rather than 312 * {@link #getManifestApkVersion(Context)} or {@link BuildConfig#VERSION_CODE}. 313 */ 314 public static int getCanonicalVersionCode() { 315 return BuildConfig.CANONICAL_VERSION_CODE; 316 } 317 318 /** 319 * {@link BuildConfig#VERSION_CODE} may not be the actual version due to ABI split code adding a 320 * postfix after BuildConfig is generated. 321 * <p> 322 * However, in most cases you want to use {@link BuildConfig#CANONICAL_VERSION_CODE} via 323 * {@link #getCanonicalVersionCode()} 324 */ 325 public static int getManifestApkVersion(Context context) { 326 try { 327 return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; 328 } catch (PackageManager.NameNotFoundException e) { 329 throw new AssertionError(e); 330 } 331 } 332 333 public static String getSecret(int size) { 334 byte[] secret = getSecretBytes(size); 335 return Base64.encodeWithPadding(secret); 336 } 337 338 public static byte[] getSecretBytes(int size) { 339 return getSecretBytes(new SecureRandom(), size); 340 } 341 342 public static byte[] getSecretBytes(@NonNull SecureRandom secureRandom, int size) { 343 byte[] secret = new byte[size]; 344 secureRandom.nextBytes(secret); 345 return secret; 346 } 347 348 /** 349 * @return The amount of time (in ms) until this build of Signal will be considered 'expired'. 350 * Takes into account both the build age as well as any remote deprecation values. 351 */ 352 public static long getTimeUntilBuildExpiry() { 353 if (SignalStore.misc().isClientDeprecated()) { 354 return 0; 355 } 356 357 long buildAge = System.currentTimeMillis() - BuildConfig.BUILD_TIMESTAMP; 358 long timeUntilBuildDeprecation = BUILD_LIFESPAN - buildAge; 359 long timeUntilRemoteDeprecation = RemoteDeprecation.getTimeUntilDeprecation(); 360 361 if (timeUntilRemoteDeprecation != -1) { 362 long timeUntilDeprecation = Math.min(timeUntilBuildDeprecation, timeUntilRemoteDeprecation); 363 return Math.max(timeUntilDeprecation, 0); 364 } else { 365 return Math.max(timeUntilBuildDeprecation, 0); 366 } 367 } 368 369 public static <T> T getRandomElement(T[] elements) { 370 return elements[new SecureRandom().nextInt(elements.length)]; 371 } 372 373 public static <T> T getRandomElement(List<T> elements) { 374 return elements.get(new SecureRandom().nextInt(elements.size())); 375 } 376 377 public static boolean equals(@Nullable Object a, @Nullable Object b) { 378 return a == b || (a != null && a.equals(b)); 379 } 380 381 public static int hashCode(@Nullable Object... objects) { 382 return Arrays.hashCode(objects); 383 } 384 385 public static @Nullable Uri uri(@Nullable String uri) { 386 if (uri == null) return null; 387 else return Uri.parse(uri); 388 } 389 390 public static boolean isLowMemory(Context context) { 391 ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 392 393 return activityManager.isLowRamDevice() || activityManager.getLargeMemoryClass() <= 64; 394 } 395 396 public static int clamp(int value, int min, int max) { 397 return Math.min(Math.max(value, min), max); 398 } 399 400 public static long clamp(long value, long min, long max) { 401 return Math.min(Math.max(value, min), max); 402 } 403 404 public static float clamp(float value, float min, float max) { 405 return Math.min(Math.max(value, min), max); 406 } 407 408 /** 409 * Returns half of the difference between the given length, and the length when scaled by the 410 * given scale. 411 */ 412 public static float halfOffsetFromScale(int length, float scale) { 413 float scaledLength = length * scale; 414 return (length - scaledLength) / 2; 415 } 416 417 public static @Nullable String readTextFromClipboard(@NonNull Context context) { 418 { 419 ClipboardManager clipboardManager = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE); 420 421 if (clipboardManager.hasPrimaryClip() && clipboardManager.getPrimaryClip().getItemCount() > 0) { 422 return clipboardManager.getPrimaryClip().getItemAt(0).getText().toString(); 423 } else { 424 return null; 425 } 426 } 427 } 428 429 public static void writeTextToClipboard(@NonNull Context context, @NonNull String text) { 430 writeTextToClipboard(context, context.getString(R.string.app_name), text); 431 } 432 433 public static void writeTextToClipboard(@NonNull Context context, @NonNull String label, @NonNull String text) { 434 android.content.ClipboardManager clipboard = (android.content.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); 435 ClipData clip = ClipData.newPlainText(label, text); 436 clipboard.setPrimaryClip(clip); 437 } 438 439 public static int toIntExact(long value) { 440 if ((int)value != value) { 441 throw new ArithmeticException("integer overflow"); 442 } 443 return (int)value; 444 } 445 446 public static boolean isEquals(@Nullable Long first, long second) { 447 return first != null && first == second; 448 } 449 450 public static String getPrettyFileSize(long sizeBytes) { 451 return MemoryUnitFormat.formatBytes(sizeBytes); 452 } 453 454 public static void copyToClipboard(@NonNull Context context, @NonNull CharSequence text) { 455 ServiceUtil.getClipboardManager(context).setPrimaryClip(ClipData.newPlainText(COPY_LABEL, text)); 456 } 457 458 @SafeVarargs 459 public static <T> List<T> concatenatedList(Collection <T>... items) { 460 final List<T> concat = new ArrayList<>(Stream.of(items).reduce(0, (sum, list) -> sum + list.size())); 461 462 for (Collection<T> list : items) { 463 concat.addAll(list); 464 } 465 466 return concat; 467 } 468 469 public static boolean isLong(String value) { 470 try { 471 Long.parseLong(value); 472 return true; 473 } catch (NumberFormatException e) { 474 return false; 475 } 476 } 477 478 public static int parseInt(String integer, int defaultValue) { 479 try { 480 return Integer.parseInt(integer); 481 } catch (NumberFormatException e) { 482 return defaultValue; 483 } 484 } 485}