/* * VoIP.ms SMS * Copyright (C) 2015-2016 Michael Kourlas * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.kourlas.voipms_sms.utils; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.graphics.*; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.os.Build; import android.provider.ContactsContract; import android.provider.Settings; import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; import android.view.View; import android.view.ViewOutlineProvider; import net.kourlas.voipms_sms.R; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; /** * Contains various common utility methods. */ @SuppressWarnings("WeakerAccess") public class Utils { /** * Gets the name of a contact from the Android contacts provider, given a * phone number. * * @param applicationContext The application context. * @param phoneNumber The phone number of the contact. * @return The name of the contact. */ public static String getContactName(Context applicationContext, String phoneNumber) { try { Uri uri = Uri.withAppendedPath( ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); Cursor cursor = applicationContext.getContentResolver().query( uri, new String[] { ContactsContract.PhoneLookup._ID, ContactsContract.PhoneLookup.DISPLAY_NAME }, null, null, null); if (cursor != null) { if (cursor.moveToFirst()) { String name = cursor.getString(cursor.getColumnIndex( ContactsContract.PhoneLookup.DISPLAY_NAME)); cursor.close(); return name; } else { cursor.close(); } } return null; } catch (SecurityException ex) { return null; } } /** * Gets a URI pointing to a contact's photo, given a phone number. * * @param applicationContext The application context. * @param phoneNumber The phone number of the contact. * @return A URI pointing to the contact's photo. */ public static String getContactPhotoUri(Context applicationContext, String phoneNumber) { try { Uri uri = Uri.withAppendedPath( ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber)); return getContactPhotoUri(applicationContext, uri); } catch (SecurityException ex) { return null; } } /** * Gets a URI pointing to a contact's photo, given the URI for that contact. * * @param applicationContext The application context. * @param uri The URI of the contact. * @return A URI pointing to the contact's photo. */ public static String getContactPhotoUri(Context applicationContext, Uri uri) { try { Cursor cursor = applicationContext.getContentResolver().query( uri, new String[] { ContactsContract.PhoneLookup._ID, ContactsContract.PhoneLookup.PHOTO_THUMBNAIL_URI }, null, null, null); if (cursor != null) { if (cursor.moveToFirst()) { String photoUri = cursor.getString(cursor.getColumnIndex( ContactsContract.Contacts.PHOTO_THUMBNAIL_URI)); cursor.close(); return photoUri; } else { cursor.close(); } } return null; } catch (SecurityException ex) { return null; } } /** * Formats a date for display in the application. * * @param applicationContext The application context. * @param date The date to format. * @param hideTime Omits the time in the formatted date if true. * @return The formatted date. */ public static String getFormattedDate(Context applicationContext, Date date, boolean hideTime) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); Calendar oneMinuteAgo = Calendar.getInstance(); oneMinuteAgo.add(Calendar.MINUTE, -1); if (oneMinuteAgo.getTime().before(date)) { // Last minute: X seconds ago long seconds = (Calendar.getInstance().getTime().getTime() - calendar.getTime().getTime()) / 1000; if (seconds < 10) { return applicationContext .getString(R.string.utils_date_just_now); } else { return seconds + " " + applicationContext .getString(R.string.utils_date_seconds_ago); } } Calendar oneHourAgo = Calendar.getInstance(); oneHourAgo.add(Calendar.HOUR_OF_DAY, -1); if (oneHourAgo.getTime().before(date)) { // Last hour: X minutes ago long minutes = (Calendar.getInstance().getTime().getTime() - calendar.getTime().getTime()) / (1000 * 60); if (minutes == 1) { return applicationContext .getString(R.string.utils_date_one_minute_ago); } else { return minutes + " " + applicationContext .getString(R.string.utils_date_minutes_ago); } } if (compareDateWithoutTime(Calendar.getInstance(), calendar) == 0) { // Today: h:mm a DateFormat format = new SimpleDateFormat("h:mm a", Locale.getDefault()); return format.format(date); } if (Calendar.getInstance().get(Calendar.WEEK_OF_YEAR) == calendar.get(Calendar.WEEK_OF_YEAR) && Calendar.getInstance().get(Calendar.YEAR) == calendar.get(Calendar.YEAR)) { if (hideTime) { // This week: EEE DateFormat format = new SimpleDateFormat("EEE", Locale.getDefault()); return format.format(date); } else { // This week: EEE h:mm a DateFormat format = new SimpleDateFormat("EEE h:mm a", Locale.getDefault()); return format.format(date); } } if (Calendar.getInstance().get(Calendar.YEAR) == calendar.get(Calendar.YEAR)) { if (hideTime) { // This year: MMM d DateFormat format = new SimpleDateFormat("MMM d", Locale.getDefault()); return format.format(date); } else { // This year: MMM d h:mm a DateFormat format = new SimpleDateFormat("MMM d, h:mm a", Locale.getDefault()); return format.format(date); } } if (hideTime) { // Any: MMM d, yyyy DateFormat format = new SimpleDateFormat("MMM d, yyyy", Locale.getDefault()); return format.format(date); } else { // Any: MMM d, yyyy h:mm a DateFormat format = new SimpleDateFormat("MMM d, yyyy, h:mm a", Locale.getDefault()); return format.format(date); } } /** * Returns true if two dates are equivalent (excluding times). * * @param c1 The first date to compare. * @param c2 The second date to compare. * @return True if the two dates are equivalent (excluding times). */ private static int compareDateWithoutTime(Calendar c1, Calendar c2) { if (c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR)) { return c1.get(Calendar.YEAR) - c2.get(Calendar.YEAR); } if (c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH)) { return c1.get(Calendar.MONTH) - c2.get(Calendar.MONTH); } return c1.get(Calendar.DAY_OF_MONTH) - c2.get(Calendar.DAY_OF_MONTH); } /** * Returns a phone number for display in the application. * * @param phoneNumber The phone number to format. * @return The formatted phone number. */ public static String getFormattedPhoneNumber(String phoneNumber) { if (phoneNumber.length() == 10) { MessageFormat phoneNumberFormat = new MessageFormat("({0}) {1}-{2}"); String[] phoneNumberArray = new String[] { phoneNumber.substring(0, 3), phoneNumber.substring(3, 6), phoneNumber.substring(6) }; phoneNumber = phoneNumberFormat.format(phoneNumberArray); } else if (phoneNumber.length() == 11 && phoneNumber.charAt(0) == '1') { MessageFormat phoneNumberFormat = new MessageFormat("({0}) {1}-{2}"); String[] phoneNumberArray = new String[] { phoneNumber.substring(1, 4), phoneNumber.substring(4, 7), phoneNumber.substring(7) }; phoneNumber = phoneNumberFormat.format(phoneNumberArray); } return phoneNumber; } /** * Retrieves a JSON object from the specified URL. * <p/> * Note that this is a blocking method; it should not be called from the * URI thread. * * @param urlString The URL to retrieve the JSON from. * @return The JSON object at the specified URL. * @throws IOException If a connection to the server could not be * established. * @throws JSONException If the server did not return valid JSON. */ public static JSONObject getJson(String urlString) throws IOException, JSONException { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setReadTimeout(15000); connection.setConnectTimeout(15000); connection.setRequestMethod("GET"); connection.setDoInput(true); connection.connect(); BufferedReader reader = new BufferedReader( new InputStreamReader(connection.getInputStream(), "UTF-8")); StringBuilder data = new StringBuilder(); String newLine = System.getProperty("line.separator"); String line; while ((line = reader.readLine()) != null) { data.append(line); data.append(newLine); } reader.close(); return new JSONObject(data.toString()); } /** * Checks if the Internet connection is available. * * @param applicationContext The application context. * @return True if the Internet connection is available, false otherwise. */ public static boolean isNetworkConnectionAvailable( Context applicationContext) { ConnectivityManager connMgr = (ConnectivityManager) applicationContext.getSystemService( Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); return networkInfo != null && networkInfo.isConnected(); } /** * Applies a circular mask to a view. * <p/> * Note that this method only works on Lollipop and above; it will * silently fail on older versions. * * @param view The view to apply the mask to. */ public static void applyCircularMask(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { view.setOutlineProvider(new ViewOutlineProvider() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void getOutline(View view, Outline outline) { outline.setOval(0, 0, view.getWidth(), view.getHeight()); } }); view.setClipToOutline(true); } } /** * Applies a circular mask to a bitmap. * * @param bitmap The bitmap to apply the mask to. */ public static Bitmap applyCircularMask(Bitmap bitmap) { final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); final Canvas canvas = new Canvas(output); final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); canvas.drawARGB(0, 0, 0, 0); canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint); paint.setAntiAlias(true); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } /** * Applies a rectangular rounded corners mask to a view. * <p/> * Note that this method only works on Lollipop and above; it will * silently fail on older versions. * * @param view The view to apply the mask to. */ public static void applyRoundedCornersMask(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { view.setOutlineProvider(new ViewOutlineProvider() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void getOutline(View view, Outline outline) { outline .setRoundRect(0, 0, view.getWidth(), view.getHeight(), 15); } }); view.setClipToOutline(true); } } /** * Shows a standard information dialog to the user. * * @param context The source context. * @param text The text of the dialog. */ public static void showInfoDialog(Context context, String text) { showAlertDialog(context, null, text, context.getString(R.string.ok), null, null, null); } /** * Shows an alert dialog to the user. * * @param context The source context. * @param title The title of the dialog. * @param text The text of the dialog. * @param positiveButtonText The text of the positive button of the * dialog. * @param positiveButtonAction The action to be taken when the positive * button is clicked. * @param negativeButtonText The text of the negative button of the * dialog. * @param negativeButtonAction The action to be taken when the negative * button is clicked. */ public static AlertDialog showAlertDialog( Context context, String title, String text, String positiveButtonText, DialogInterface.OnClickListener positiveButtonAction, String negativeButtonText, DialogInterface.OnClickListener negativeButtonAction) { AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.DialogTheme); builder.setMessage(text); builder.setTitle(title); builder.setPositiveButton(positiveButtonText, positiveButtonAction); builder.setNegativeButton(negativeButtonText, negativeButtonAction); builder.setCancelable(false); return builder.show(); } /** * Shows a snackbar requesting a permission with a button linking to the * application settings page. * * @param activity The host activity for the snackbar. * @param viewId The ID of the view to add the snackbar to. * @param text The text to display. */ public static void showPermissionSnackbar(Activity activity, int viewId, String text) { final View view = activity.findViewById(viewId); Snackbar snackbar = Snackbar.make( view, text, Snackbar.LENGTH_LONG); snackbar.setAction( R.string.settings, v -> { Intent intent = new Intent(); intent.setAction( Settings .ACTION_APPLICATION_DETAILS_SETTINGS); intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags( Intent.FLAG_ACTIVITY_NO_HISTORY); intent.addFlags( Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); Uri uri = Uri.fromParts( "package", activity.getPackageName(), null); intent.setData(uri); activity.startActivity(intent); }); snackbar.show(); } /** * Returns a string consisting only of the digits in the specified string. * * @param str The specified string. * @return A string consisting only of the digits in the specified string. */ public static String getDigitsOfString(String str) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); if (Character.isDigit(c)) { stringBuilder.append(c); } } return stringBuilder.toString(); } /** * Returns the string representation of the specified phone number type. * * @param type The specified phone number type. * @return The string representation of the specified phone number type. */ public static String getPhoneNumberType(int type) { switch (type) { case ContactsContract.CommonDataKinds.Phone.TYPE_HOME: return "Home"; case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE: return "Mobile"; case ContactsContract.CommonDataKinds.Phone.TYPE_WORK: return "Work"; case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME: return "Home Fax"; case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK: return "Work Fax"; case ContactsContract.CommonDataKinds.Phone.TYPE_MAIN: return "Main"; case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER: return "Other"; case ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM: return "Custom"; case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER: return "Pager"; default: return ""; } } }