/* * ____.____ __.____ ___ _____ * | | |/ _| | \ / _ \ ______ ______ * | | < | | / / /_\ \\____ \\____ \ * /\__| | | \| | / / | \ |_> > |_> > * \________|____|__ \______/ \____|__ / __/| __/ * \/ \/|__| |__| * * Copyright (c) 2014-2015 Paul "Marunjar" Pretsch * * 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 <http://www.gnu.org/licenses/> */ package org.voidsink.anewjkuapp.utils; import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.annotation.SuppressLint; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.SearchManager; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v7.preference.PreferenceManager; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; import org.voidsink.anewjkuapp.ImportPoiTask; import org.voidsink.anewjkuapp.KusssAuthenticator; import org.voidsink.anewjkuapp.KusssContentContract; import org.voidsink.anewjkuapp.PreferenceWrapper; import org.voidsink.anewjkuapp.R; import org.voidsink.anewjkuapp.activity.MainActivity; import org.voidsink.anewjkuapp.analytics.Analytics; import org.voidsink.anewjkuapp.calendar.CalendarContractWrapper; import org.voidsink.anewjkuapp.calendar.CalendarUtils; import org.voidsink.anewjkuapp.fragment.MapFragment; import org.voidsink.anewjkuapp.kusss.Assessment; import org.voidsink.anewjkuapp.kusss.AssessmentType; import org.voidsink.anewjkuapp.kusss.Course; import org.voidsink.anewjkuapp.kusss.Curriculum; import org.voidsink.anewjkuapp.kusss.Grade; import org.voidsink.anewjkuapp.kusss.LvaState; import org.voidsink.anewjkuapp.kusss.LvaWithGrade; import org.voidsink.anewjkuapp.kusss.Term; import org.voidsink.anewjkuapp.service.SyncAlarmService; import org.voidsink.anewjkuapp.update.ImportCurriculaTask; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class AppUtils { private static final String DEFAULT_POI_FILE_NAME = "JKU.gpx"; private static final String TAG = AppUtils.class.getSimpleName(); private static final Comparator<Course> CourseComparator = new Comparator<Course>() { @Override public int compare(Course lhs, Course rhs) { int value = lhs.getTitle().compareTo(rhs.getTitle()); if (value == 0) { value = lhs.getTerm().compareTo(rhs.getTerm()); } return value; } }; private static final Comparator<LvaWithGrade> LvaWithGradeComparator = new Comparator<LvaWithGrade>() { @Override public int compare(LvaWithGrade lhs, LvaWithGrade rhs) { int value = lhs.getState().compareTo(rhs.getState()); if (value == 0) { value = lhs.getCourse().getTitle() .compareTo(rhs.getCourse().getTitle()); } if (value == 0) { value = TermComparator.compare(lhs.getCourse().getTerm(), rhs.getCourse().getTerm()); } return value; } }; private static final Comparator<Curriculum> CurriculaComparator = new Comparator<Curriculum>() { @Override public int compare(Curriculum lhs, Curriculum rhs) { int value = lhs.getUni().compareToIgnoreCase(rhs.getUni()); if (value == 0) { value = lhs.getDtStart().compareTo(rhs.getDtStart()); } if (value == 0) { value = lhs.getCid().compareTo(rhs.getCid()); } return value; } }; private static final Comparator<Assessment> AssessmentComparator = new Comparator<Assessment>() { @Override public int compare(Assessment lhs, Assessment rhs) { int value = lhs.getAssessmentType().compareTo(rhs.getAssessmentType()); if (value == 0) { value = rhs.getDate().compareTo(lhs.getDate()); } if (value == 0) { value = TermComparator.compare(rhs.getTerm(), lhs.getTerm()); } if (value == 0) { value = lhs.getTitle().compareTo(rhs.getTitle()); } return value; } }; private static final Comparator<Term> TermComparator = new Comparator<Term>() { @Override public int compare(Term lhs, Term rhs) { if (lhs == null && rhs == null) return 0; if (lhs == null) return -1; if (rhs == null) return 1; return rhs.compareTo(lhs); } }; public static void doOnNewVersion(Context context) { int mLastVersion = PreferenceWrapper.getLastVersion(context); int mCurrentVersion = PreferenceWrapper.getCurrentVersion(context); if (mLastVersion != mCurrentVersion || mLastVersion == PreferenceWrapper.PREF_LAST_VERSION_NONE) { boolean errorOccured = false; try { if (!initPreferences(context)) { errorOccured = true; } if (!importDefaultPois(context)) { errorOccured = true; } if (!copyDefaultMap(context)) { errorOccured = true; } if (shouldRemoveOldAccount(mLastVersion, mCurrentVersion)) { if (!removeAccount(context)) { errorOccured = true; } } if (shouldImportCurricula(mLastVersion, mCurrentVersion)) { if (!importCurricula(context)) { errorOccured = true; } } if (shouldRecreateCalendars(mLastVersion, mCurrentVersion)) { if (!removeCalendars(context)) { errorOccured = true; } if (!createCalendars(context)) { errorOccured = true; } } else if (shouldDeleteKusssEvents(mLastVersion, mCurrentVersion)) { if (!deleteKusssEvents(context)) { errorOccured = true; } } PreferenceWrapper.applySyncInterval(context); } catch (Exception e) { Log.e(TAG, "doOnNewVersion failed", e); Analytics.sendException(context, e, false); errorOccured = true; } if (!errorOccured) { PreferenceWrapper.setLastVersion(context, mCurrentVersion); } } } private static boolean createCalendars(Context context) { Account account = AppUtils.getAccount(context); return (account == null || CalendarUtils.createCalendarsIfNecessary(context, account)); } private static boolean deleteKusssEvents(Context context) { Account account = AppUtils.getAccount(context); return (account == null || CalendarUtils.deleteKusssEvents(context, account)); } private static boolean removeCalendars(Context context) { return CalendarUtils.removeCalendar(context, CalendarUtils.ARG_CALENDAR_EXAM) && CalendarUtils.removeCalendar(context, CalendarUtils.ARG_CALENDAR_COURSE); } private static boolean shouldRecreateCalendars(int lastVersion, int currentVersion) { // calendars changed with 140029 // remove old calendars on startup to avoid strange behaviour return (lastVersion <= 140028 && currentVersion > 140028); } private static boolean shouldDeleteKusssEvents(int lastVersion, int currentVersion) { // events changed with 140053 return (lastVersion <= 140052 && currentVersion > 140052); } private static boolean shouldImportCurricula(int lastVersion, int currentVersion) { // curricula added with 140026 // import on startup to avoid strange behaviour and missing tabs return (lastVersion < 100026 && currentVersion >= 100026) || (lastVersion < 140026 && currentVersion >= 140026); } private static boolean importCurricula(Context context) { Account account = getAccount(context); return account == null || executeEm(context, new Callable<?>[]{new ImportCurriculaTask(account, context)}, false); } private static boolean removeAccount(Context context) { Account account = getAccount(context); if (account != null) { removeAccout(AccountManager.get(context), account); } return true; } public static void removeAccout(AccountManager accountManager, Account account) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { accountManager.removeAccount(account, null, null, null); } else { accountManager.removeAccount(account, null, null); } Log.d(TAG, "account removed"); } private static boolean shouldRemoveOldAccount(int lastVersion, int currentVersion) { // calendar names changed with 100017, remove account for avoiding // corrupted data return (lastVersion < 100017 && currentVersion >= 100017) || (lastVersion < 140017 && currentVersion >= 140017); } private static boolean initPreferences(Context context) { try { PreferenceManager.setDefaultValues(context, R.xml.preference_app, true); PreferenceManager.setDefaultValues(context, R.xml.preference_dashclock_extension_mensa, true); PreferenceManager.setDefaultValues(context, R.xml.preference_kusss, true); } catch (Exception e) { Log.e(TAG, "initPreferences", e); return false; } return true; } private static boolean copyDefaultMap(Context context) { try { // write file to sd for mapsforge OutputStream mapFileWriter = new BufferedOutputStream( context.openFileOutput(MapFragment.MAP_FILE_NAME, Context.MODE_PRIVATE)); InputStream assetData = new BufferedInputStream(context.getAssets() .open(MapFragment.MAP_FILE_NAME)); byte[] buffer = new byte[1024]; int len = assetData.read(buffer); while (len != -1) { mapFileWriter.write(buffer, 0, len); len = assetData.read(buffer); } mapFileWriter.close(); } catch (FileNotFoundException e) { Log.e(TAG, "copyDefaultMap", e); return false; } catch (IOException e) { Log.e(TAG, "copyDefaultMap", e); return false; } return true; } private static boolean importDefaultPois(Context context) { // import JKU Pois try { // write file to sd for import OutputStream mapFileWriter = new BufferedOutputStream( context.openFileOutput(DEFAULT_POI_FILE_NAME, Context.MODE_PRIVATE)); InputStream assetData = new BufferedInputStream(context.getAssets() .open(DEFAULT_POI_FILE_NAME)); byte[] buffer = new byte[1024]; int len = assetData.read(buffer); while (len != -1) { mapFileWriter.write(buffer, 0, len); len = assetData.read(buffer); } mapFileWriter.close(); // import file return executeEm(context, new Callable<?>[]{new ImportPoiTask(context, new File(context.getFilesDir(), DEFAULT_POI_FILE_NAME), true)}, false); } catch (IOException e) { Analytics.sendException(context, e, false); return false; } } public static double getECTS(LvaState state, List<LvaWithGrade> lvas) { double sum = 0; for (LvaWithGrade lva : lvas) { if (state == LvaState.ALL || state == lva.getState()) { sum += lva.getCourse().getEcts(); } } return sum; } public static void sortCourses(List<Course> courses) { Collections.sort(courses, CourseComparator); } private static void sortLVAsWithGrade(List<LvaWithGrade> mCourses) { Collections.sort(mCourses, LvaWithGradeComparator); } public static void sortAssessments(List<Assessment> assessments) { Collections.sort(assessments, AssessmentComparator); } private static void removeDuplicates(List<LvaWithGrade> mCourses) { // remove done duplicates int i = 0; while (i < mCourses.size()) { if (mCourses.get(i).getState() == LvaState.DONE) { Course course = mCourses.get(i).getCourse(); int j = i + 1; while (j < mCourses.size()) { Course nextCourse = mCourses.get(j).getCourse(); if (course.getCode().equals(nextCourse.getCode()) && course.getTitle().equals(nextCourse.getTitle())) { mCourses.remove(j); Log.d("removeDuplicates", "remove from done " + nextCourse.getCode() + " " + nextCourse.getTitle()); } else { j++; } } } i++; } // remove all other duplicates i = 0; while (i < mCourses.size()) { if (mCourses.get(i).getState() != LvaState.DONE) { Course course = mCourses.get(i).getCourse(); int j = i + 1; while (j < mCourses.size()) { Course nextCourse = mCourses.get(j).getCourse(); if (course.getCode().equals(nextCourse.getCode()) && course.getTitle().equals(nextCourse.getTitle())) { mCourses.remove(j); Log.d("removeDuplicates", "remove from other " + nextCourse.getCode() + " " + nextCourse.getTitle()); } else { j++; } } } i++; } } public static void removeDuplicates(List<LvaWithGrade> mDoneLvas, List<LvaWithGrade> mOpenLvas) { // Log.i("removeDuplicates", "---------"); // for (LvaWithGrade lva : mDoneLvas) { // Log.i("removeDuplicates", "done: " + lva.getLva().getCode() + " " // + lva.getLva().getTitle()); // } // for (LvaWithGrade lva : mOpenLvas) { // Log.i("removeDuplicates", "open: " + lva.getLva().getCode() + " " // + lva.getLva().getTitle()); // } int i = 0; while (i < mDoneLvas.size()) { Course course = mDoneLvas.get(i).getCourse(); int j = i + 1; while (j < mDoneLvas.size()) { Course nextCourse = mDoneLvas.get(j).getCourse(); if (course.getCode().equals(nextCourse.getCode()) && course.getTitle().equals(nextCourse.getTitle())) { mDoneLvas.remove(j); Log.d("removeDuplicates", "remove from done " + nextCourse.getCode() + " " + nextCourse.getTitle()); } else { j++; } } j = 0; while (j < mOpenLvas.size()) { Course nextCourse = mOpenLvas.get(j).getCourse(); if (course.getCode().equals(nextCourse.getCode()) && course.getTitle().equals(nextCourse.getTitle())) { mOpenLvas.remove(j); Log.d("removeDuplicates", "remove from open " + nextCourse.getCode() + " " + nextCourse.getTitle()); } else { j++; } } i++; } i = 0; while (i < mOpenLvas.size()) { Course course = mOpenLvas.get(i).getCourse(); int j = i + 1; while (j < mOpenLvas.size()) { Course nextCourse = mOpenLvas.get(j).getCourse(); if (course.getCode().equals(nextCourse.getCode()) && course.getTitle().equals(nextCourse.getTitle())) { mOpenLvas.remove(j); Log.d("removeDuplicates", "remove from open " + nextCourse.getCode() + " " + nextCourse.getTitle()); } else { j++; } } i++; } } public static double getAvgGrade(List<Assessment> assessments, boolean ectsWeighting, AssessmentType type, boolean positiveOnly) { double sum = 0; double count = 0; if (assessments != null) { for (Assessment assessment : assessments) { if (type == AssessmentType.ALL || type == assessment.getAssessmentType()) { if (!positiveOnly || assessment.getGrade().isPositive()) { if (!ectsWeighting) { sum += assessment.getGrade().getValue(); count++; } else { sum += assessment.getEcts() * assessment.getGrade().getValue(); count += assessment.getEcts(); } } } } } if (count == 0) { return 0; } else { return sum / count; } } public static Account getAccount(Context context) { if (context == null) { return null; } if (ContextCompat.checkSelfPermission(context, Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED) { Log.w(TAG, "getAccount failed, no permission"); return null; } // get first account Account[] accounts = AccountManager.get(context).getAccountsByType( KusssAuthenticator.ACCOUNT_TYPE); if (accounts.length == 0) { return null; } return accounts[0]; } public static String getAccountName(Context context, Account account) { if (account == null) { return null; } return account.name; } public static String getAccountPassword(Context context, Account account) { if (account == null) { return null; } return AccountManager.get(context).getPassword(account); } @SuppressLint("NewApi") public static String getAccountAuthToken(Context context, Account account) { if (account == null) { return null; } AccountManager am = AccountManager.get(context); AccountManagerFuture<Bundle> response = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { response = am.getAuthToken(account, KusssAuthenticator.AUTHTOKEN_TYPE_READ_ONLY, null, true, null, null); } else { response = am.getAuthToken(account, KusssAuthenticator.AUTHTOKEN_TYPE_READ_ONLY, null, true, null, null); } if (response == null) return null; try { return response.getResult().getString(AccountManager.KEY_AUTHTOKEN); } catch (OperationCanceledException | AuthenticatorException | IOException e) { Log.e(TAG, "getAccountAuthToken", e); return null; } } public static double getGradePercent(List<Assessment> assessments, Grade grade, boolean ectsWeighting) { if (assessments == null || assessments.size() == 0) return 0; double count = 0; double sum = 0; double value; for (Assessment assessment : assessments) { if (!ectsWeighting) { value = 1; } else { value = assessment.getEcts(); } if (assessment.getGrade().equals(grade)) { count += value; } sum += value; } return count / sum * 100; } public static double getGradeEcts(List<Assessment> assessments, Grade grade) { if (assessments == null || assessments.size() == 0) return 0; double sum = 0; for (Assessment assessment : assessments) { if (assessment.getGrade().equals(grade)) { sum += assessment.getEcts(); } } return sum; } public static List<LvaWithGrade> getLvasWithGrades(List<Term> terms, List<Course> courses, List<Assessment> assessments, boolean withAssessmentOnly, Term forLastTerm) { List<LvaWithGrade> result = new ArrayList<>(); Map<String, Term> termMap = null; if (terms != null) { termMap = new HashMap<>(); for (Term term : terms) { termMap.put(term.toString(), term); } } for (Course course : courses) { if (termMap == null || termMap.containsKey(course.getTerm().toString())) { Assessment assessment = findAssessment(assessments, course); if (!withAssessmentOnly || assessment != null || (forLastTerm != null && forLastTerm.equals(course.getTerm()))) { result.add(new LvaWithGrade(course, assessment)); } } } AppUtils.removeDuplicates(result); AppUtils.sortLVAsWithGrade(result); return result; } private static Assessment findAssessment(List<Assessment> assessments, Course course) { Assessment finalAssessment = null; for (Assessment assessment : assessments) { if (assessment.getCode().equals(course.getCode()) && assessment.getCourseId().equals(course.getCourseId())) { if (finalAssessment == null || finalAssessment.getGrade().getValue() > assessment.getGrade().getValue()) { finalAssessment = assessment; } } } if (finalAssessment == null) { for (Assessment assessment : assessments) { if (assessment.getCode().equals(course.getCode()) && (assessment.getTitle().contains(course.getTitle()) || course.getTitle().contains(assessment.getTitle()))) { if (finalAssessment == null || finalAssessment.getGrade().getValue() > assessment.getGrade().getValue()) { finalAssessment = assessment; } } } if (finalAssessment != null) { Log.d(TAG, String.format("found by code/title: %s/%s -> %s", course.getCode(), course.getTitle(), course.getCourseId())); } } else { Log.d(TAG, String.format("found by code/courseId: %s/%s -> %s", course.getCode(), course.getCourseId(), course.getTitle())); } return finalAssessment; } private static void addIfRecent(List<Assessment> assessments, Assessment assessment) { if (!assessment.getAssessmentType().isDuplicatesPossible()) { int i = 0; while (i < assessments.size()) { Assessment g = assessments.get(i); // check only assessments for same lva and term if (g.getCode().equals(assessment.getCode()) && g.getCourseId().equals(assessment.getCourseId())) { // keep only recent (best and newest) assessment if (g.getDate().before(assessment.getDate())) { // remove last assessment assessments.remove(i); } else { // break without adding return; } } else { i++; } } } // finally add assessment assessments.add(assessment); } public static List<Assessment> filterAssessments(List<Term> terms, List<Assessment> assessments) { List<Assessment> result = new ArrayList<>(); Map<String, Term> termMap = null; if (terms != null) { termMap = new HashMap<>(); for (Term term : terms) { termMap.put(term.toString(), term); } } if (assessments != null) { for (Assessment assessment : assessments) { if (termMap == null || (assessment.getTerm() != null && termMap.containsKey(assessment.getTerm().toString()))) { addIfRecent(result, assessment); } } } AppUtils.sortAssessments(result); return result; } public static void sortCurricula(List<Curriculum> mCurricula) { Collections.sort(mCurricula, CurriculaComparator); } public static String getRowString(Cursor c) { if (c == null) { return null; } String row = ""; for (int i = 0; i < c.getColumnCount(); i++) { try { row = row + c.getColumnName(i) + "=" + c.getString(i) + ";"; } catch (Exception e) { row = row + "@" + Integer.toString(i) + "=?;"; } } return row; } public static void updateSyncAlarm(Context context, boolean reCreateAlarm) { boolean mIsCalendarSyncEnabled = false; boolean mIsKusssSyncEnable = false; boolean mIsMasterSyncEnabled = ContentResolver.getMasterSyncAutomatically(); if (mIsMasterSyncEnabled) { final Account mAccount = getAccount(context); if (mAccount != null) { mIsCalendarSyncEnabled = ContentResolver.getSyncAutomatically(mAccount, CalendarContractWrapper.AUTHORITY()); mIsKusssSyncEnable = ContentResolver.getSyncAutomatically(mAccount, KusssContentContract.AUTHORITY); } } Log.d(TAG, String.format("MasterSync=%b, CalendarSync=%b, KusssSync=%b", mIsMasterSyncEnabled, mIsCalendarSyncEnabled, mIsKusssSyncEnable)); AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, SyncAlarmService.class); intent.putExtra(Consts.ARG_UPDATE_CAL, !mIsCalendarSyncEnabled); intent.putExtra(Consts.ARG_UPDATE_KUSSS, !mIsKusssSyncEnable); intent.putExtra(Consts.ARG_RECREATE_SYNC_ALARM, true); intent.putExtra(Consts.SYNC_SHOW_PROGRESS, true); // check if pending intent exists reCreateAlarm = reCreateAlarm || (PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_NO_CREATE) == null); // new pending intent PendingIntent alarmIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); if (!mIsMasterSyncEnabled || !mIsCalendarSyncEnabled || !mIsKusssSyncEnable) { if (reCreateAlarm) { long interval = PreferenceWrapper.getSyncInterval(context) * DateUtils.HOUR_IN_MILLIS; // synchronize in half an hour am.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + AlarmManager.INTERVAL_HALF_HOUR, interval, alarmIntent); } } else { am.cancel(alarmIntent); } } public static void triggerSync(Context context, Account account, boolean syncCalendar, boolean syncKusss) { try { if (context == null || account == null) { return; } if (syncCalendar || syncKusss) { Bundle b = new Bundle(); // Disable sync backoff and ignore sync preferences. In other // words...perform sync NOW! b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); b.putBoolean(Consts.SYNC_SHOW_PROGRESS, true); if (syncCalendar) { ContentResolver.requestSync(account, // Sync CalendarContractWrapper.AUTHORITY(), // Calendar Content authority b); // Extras } if (syncKusss) { ContentResolver.requestSync(account, // Sync KusssContentContract.AUTHORITY, // KUSSS Content authority b); // Extras } } } catch (Exception e) { Analytics.sendException(context, e, true); } } public static String getTimeString(Date dtStart, Date dtEnd) { DateFormat dfStart = DateFormat.getTimeInstance(DateFormat.SHORT); DateFormat dfEnd = DateFormat.getTimeInstance(DateFormat.SHORT); if (!CalendarUtils.isSameDay(dtStart, dtEnd)) { dfEnd = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); } return String.format("%s - %s", dfStart.format(dtStart), dfEnd.format(dtEnd)); } public static String getEventString(long eventDTStart, long eventDTEnd, String eventTitle) { int index = eventTitle.indexOf(", "); if (index > 1) { eventTitle = eventTitle.substring(0, index); } DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); return String.format("%s: %s, %s", df.format(eventDTStart), AppUtils.getTimeString(new Date(eventDTStart), new Date(eventDTEnd)), eventTitle); } public static String termToString(Term term) { if (term != null) { return term.toString(); } return ""; } public static void showEventInCalendar(Context context, long eventId, long dtStart) { if (eventId > 0) { Uri uri = ContentUris.withAppendedId(CalendarContractWrapper.Events.CONTENT_URI(), eventId); Intent intent = new Intent(Intent.ACTION_VIEW) .setData(uri); context.startActivity(intent); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { Uri.Builder builder = CalendarContractWrapper.CONTENT_URI().buildUpon(); builder.appendPath("time"); ContentUris.appendId(builder, dtStart); Intent intent = new Intent(Intent.ACTION_VIEW) .setData(builder.build()); context.startActivity(intent); } } public static void showEventLocation(Context context, String location) { Intent intent = new Intent(context, MainActivity.class) .putExtra(MainActivity.ARG_SHOW_FRAGMENT_ID, R.id.nav_map) .putExtra(MainActivity.ARG_SAVE_LAST_FRAGMENT, false) .setAction(Intent.ACTION_SEARCH) .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); if (!TextUtils.isEmpty(location)) { intent.putExtra(SearchManager.QUERY, location); intent.putExtra(MainActivity.ARG_EXACT_LOCATION, true); } else { intent.putExtra(SearchManager.QUERY, "Uniteich"); } context.startActivity(intent); } public static boolean executeEm(Context context, Callable<?>[] callables, boolean wait) { boolean result = true; ExecutorService es = Executors.newSingleThreadExecutor(); try { result = executeEm(es, context, callables, wait); } finally { es.shutdown(); } return result; } public static boolean executeEm(ExecutorService es, Context context, Callable<?>[] callables, boolean wait) { boolean result = true; List<Future<?>> futures = new ArrayList<>(); for (Callable c : callables) { futures.add(es.submit(c)); } if (wait) { for (Future f : futures) { try { f.get(); } catch (InterruptedException | ExecutionException e) { Analytics.sendException(context, e, false); result = false; } } } return result; } public static int getRandomColor() { Random rand = new Random(System.currentTimeMillis()); float hue; do { hue = rand.nextFloat() * 360; } while ((Math.abs(mLastHue - hue) < 45) || (hue > 280 && hue < 320)); mLastHue = hue; float[] hsv = new float[3]; hsv[0] = hue; hsv[1] = 0.95f; hsv[2] = 0.8f; return Color.HSVToColor(hsv); } private static float mLastHue = new Random(System.currentTimeMillis()).nextFloat() * 360; public static Locale getLocale(Context context) { Locale locale = null; try { locale = context.getResources().getConfiguration().locale; } catch (Exception e) { locale = Locale.getDefault(); } return locale; } public static String format(Context c, String format, Object... args) { return String.format(getLocale(c), format, args); } }