/** * Copyright (C) 2011 Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.thoughtcrime.securesms.util; import android.annotation.SuppressLint; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Shader; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.provider.Telephony; import android.telephony.TelephonyManager; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.StyleSpan; import android.widget.EditText; import org.thoughtcrime.securesms.BuildConfig; import org.thoughtcrime.securesms.TextSecureExpiredException; import org.whispersystems.textsecure.api.util.InvalidNumberException; import org.whispersystems.textsecure.api.util.PhoneNumberFormatter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import ws.com.google.android.mms.pdu.CharacterSets; import ws.com.google.android.mms.pdu.EncodedStringValue; public class Util { public static String join(Collection list, String delimiter) { StringBuilder result = new StringBuilder(); int i=0; for (String item : list) { result.append(item); if (++i < list.size()) result.append(delimiter); } return result.toString(); } public static ExecutorService newSingleThreadedLifoExecutor() { ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingLifoQueue()); executor.execute(new Runnable() { @Override public void run() { // Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Thread.currentThread().setPriority(Thread.MIN_PRIORITY); } }); return executor; } public static boolean isEmpty(EncodedStringValue[] value) { return value == null || value.length == 0; } public static boolean isEmpty(EditText value) { return value == null || value.getText() == null || TextUtils.isEmpty(value.getText().toString()); } public static CharSequence getBoldedString(String value) { SpannableString spanned = new SpannableString(value); spanned.setSpan(new StyleSpan(Typeface.BOLD), 0, spanned.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return spanned; } public static String toIsoString(byte[] bytes) { try { return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1); } catch (UnsupportedEncodingException e) { throw new AssertionError("ISO_8859_1 must be supported!"); } } public static byte[] toIsoBytes(String isoString) { try { return isoString.getBytes(CharacterSets.MIMENAME_ISO_8859_1); } catch (UnsupportedEncodingException e) { throw new AssertionError("ISO_8859_1 must be supported!"); } } public static byte[] toUtf8Bytes(String utf8String) { try { return utf8String.getBytes(CharacterSets.MIMENAME_UTF_8); } catch (UnsupportedEncodingException e) { throw new AssertionError("UTF_8 must be supported!"); } } public static void wait(Object lock, int timeout) { try { lock.wait(timeout); } catch (InterruptedException ie) { throw new AssertionError(ie); } } public static String canonicalizeNumber(Context context, String number) throws InvalidNumberException { String localNumber = TextSecurePreferences.getLocalNumber(context); return PhoneNumberFormatter.formatNumber(number, localNumber); } public static String canonicalizeNumberOrGroup(Context context, String number) throws InvalidNumberException { if (GroupUtil.isEncodedGroup(number)) return number; else return canonicalizeNumber(context, number); } public static byte[] readFully(InputStream in) throws IOException { ByteArrayOutputStream bout = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int read; while ((read = in.read(buffer)) != -1) { bout.write(buffer, 0, read); } in.close(); return bout.toByteArray(); } public static String readFullyAsString(InputStream in) throws IOException { return new String(readFully(in)); } public static long copy(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[4096]; int read; long total = 0; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); total += read; } in.close(); out.close(); return total; } public static String getDeviceE164Number(Context context) { String localNumber = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)) .getLine1Number(); if (!TextUtils.isEmpty(localNumber) && !localNumber.startsWith("+")) { if (localNumber.length() == 10) localNumber = "+1" + localNumber; else localNumber = "+" + localNumber; return localNumber; } return null; } public static List split(String source, String delimiter) { List results = new LinkedList<>(); if (TextUtils.isEmpty(source)) { return results; } String[] elements = source.split(delimiter); Collections.addAll(results, elements); return results; } public static byte[][] split(byte[] input, int firstLength, int secondLength) { byte[][] parts = new byte[2][]; parts[0] = new byte[firstLength]; System.arraycopy(input, 0, parts[0], 0, firstLength); parts[1] = new byte[secondLength]; System.arraycopy(input, firstLength, parts[1], 0, secondLength); return parts; } public static byte[] combine(byte[]... elements) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); for (byte[] element : elements) { baos.write(element); } return baos.toByteArray(); } catch (IOException e) { throw new AssertionError(e); } } public static byte[] trim(byte[] input, int length) { byte[] result = new byte[length]; System.arraycopy(input, 0, result, 0, result.length); return result; } @SuppressLint("NewApi") public static boolean isDefaultSmsProvider(Context context){ return (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) || (context.getPackageName().equals(Telephony.Sms.getDefaultSmsPackage(context))); } public static int getCurrentApkReleaseVersion(Context context) { try { return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; } catch (PackageManager.NameNotFoundException e) { throw new AssertionError(e); } } public static String getSecret(int size) { byte[] secret = getSecretBytes(size); return Base64.encodeBytes(secret); } public static byte[] getSecretBytes(int size) { byte[] secret = new byte[size]; getSecureRandom().nextBytes(secret); return secret; } public static SecureRandom getSecureRandom() { try { return SecureRandom.getInstance("SHA1PRNG"); } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } } /* * source: http://stackoverflow.com/a/9500334 */ public static void fixBackgroundRepeat(Drawable bg) { if (bg != null) { if (bg instanceof BitmapDrawable) { BitmapDrawable bmp = (BitmapDrawable) bg; bmp.mutate(); bmp.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); } } } public static boolean isBuildFresh() { return BuildConfig.BUILD_TIMESTAMP + TimeUnit.DAYS.toMillis(180) > System.currentTimeMillis(); } }