package com.byagowi.persiancalendar.util; import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Typeface; import android.net.Uri; import android.os.Build; import android.preference.PreferenceManager; import android.support.annotation.IdRes; import android.support.annotation.RawRes; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.preference.PreferenceViewHolder; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.RelativeSizeSpan; import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.azizhuss.arabicreshaper.ArabicShaping; import com.byagowi.persiancalendar.Constants; import com.byagowi.persiancalendar.R; import com.byagowi.persiancalendar.adapter.ShapedArrayAdapter; import com.byagowi.persiancalendar.entity.CityEntity; import com.byagowi.persiancalendar.entity.DayEntity; import com.byagowi.persiancalendar.entity.EventEntity; import com.byagowi.persiancalendar.enums.CalendarTypeEnum; import com.byagowi.persiancalendar.enums.SeasonEnum; import com.byagowi.persiancalendar.service.BroadcastReceivers; import com.github.praytimes.CalculationMethod; import com.github.praytimes.Clock; import com.github.praytimes.Coordinate; import com.github.praytimes.PrayTime; import com.github.praytimes.PrayTimesCalculator; import com.github.twaddington.TypefaceSpan; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.InputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Scanner; import java.util.Set; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import calendar.AbstractDate; import calendar.CivilDate; import calendar.DateConverter; import calendar.DayOutOfRangeException; import calendar.IslamicDate; import calendar.PersianDate; import static com.byagowi.persiancalendar.Constants.AM_IN_PERSIAN; import static com.byagowi.persiancalendar.Constants.ARABIC_DIGITS; import static com.byagowi.persiancalendar.Constants.BROADCAST_ALARM; import static com.byagowi.persiancalendar.Constants.BROADCAST_RESTART_APP; import static com.byagowi.persiancalendar.Constants.DARK_THEME; import static com.byagowi.persiancalendar.Constants.DAYS_ICONS; import static com.byagowi.persiancalendar.Constants.DAYS_ICONS_AR; import static com.byagowi.persiancalendar.Constants.DEFAULT_ALTITUDE; import static com.byagowi.persiancalendar.Constants.DEFAULT_APP_LANGUAGE; import static com.byagowi.persiancalendar.Constants.DEFAULT_ATHAN_VOLUME; import static com.byagowi.persiancalendar.Constants.DEFAULT_CITY; import static com.byagowi.persiancalendar.Constants.DEFAULT_IRAN_TIME; import static com.byagowi.persiancalendar.Constants.DEFAULT_ISLAMIC_OFFSET; import static com.byagowi.persiancalendar.Constants.DEFAULT_LATITUDE; import static com.byagowi.persiancalendar.Constants.DEFAULT_LONGITUDE; import static com.byagowi.persiancalendar.Constants.DEFAULT_NOTIFY_DATE; import static com.byagowi.persiancalendar.Constants.DEFAULT_PERSIAN_DIGITS; import static com.byagowi.persiancalendar.Constants.DEFAULT_PRAY_TIME_METHOD; import static com.byagowi.persiancalendar.Constants.DEFAULT_SELECTED_WIDGET_TEXT_COLOR; import static com.byagowi.persiancalendar.Constants.DEFAULT_WIDGET_CLOCK; import static com.byagowi.persiancalendar.Constants.DEFAULT_WIDGET_IN_24; import static com.byagowi.persiancalendar.Constants.FONT_PATH; import static com.byagowi.persiancalendar.Constants.KEY_EXTRA_PRAYER_KEY; import static com.byagowi.persiancalendar.Constants.LIGHT_THEME; import static com.byagowi.persiancalendar.Constants.PERSIAN_COMMA; import static com.byagowi.persiancalendar.Constants.PERSIAN_DIGITS; import static com.byagowi.persiancalendar.Constants.PM_IN_PERSIAN; import static com.byagowi.persiancalendar.Constants.PREF_ALTITUDE; import static com.byagowi.persiancalendar.Constants.PREF_APP_LANGUAGE; import static com.byagowi.persiancalendar.Constants.PREF_ATHAN_ALARM; import static com.byagowi.persiancalendar.Constants.PREF_ATHAN_GAP; import static com.byagowi.persiancalendar.Constants.PREF_ATHAN_VOLUME; import static com.byagowi.persiancalendar.Constants.PREF_GEOCODED_CITYNAME; import static com.byagowi.persiancalendar.Constants.PREF_IRAN_TIME; import static com.byagowi.persiancalendar.Constants.PREF_ISLAMIC_OFFSET; import static com.byagowi.persiancalendar.Constants.PREF_LATITUDE; import static com.byagowi.persiancalendar.Constants.PREF_LONGITUDE; import static com.byagowi.persiancalendar.Constants.PREF_NOTIFY_DATE; import static com.byagowi.persiancalendar.Constants.PREF_PERSIAN_DIGITS; import static com.byagowi.persiancalendar.Constants.PREF_PRAY_TIME_METHOD; import static com.byagowi.persiancalendar.Constants.PREF_SELECTED_LOCATION; import static com.byagowi.persiancalendar.Constants.PREF_SELECTED_WIDGET_TEXT_COLOR; import static com.byagowi.persiancalendar.Constants.PREF_THEME; import static com.byagowi.persiancalendar.Constants.PREF_WIDGET_CLOCK; import static com.byagowi.persiancalendar.Constants.PREF_WIDGET_IN_24; /** * Common utilities that needed for this calendar * * @author ebraminio */ public class Utils { private final String TAG = Utils.class.getName(); private Context context; private Typeface typeface; private SharedPreferences prefs; private List<EventEntity> events; private PrayTimesCalculator prayTimesCalculator; private Map<PrayTime, Clock> prayTimes; private String[] persianMonths; private String[] islamicMonths; private String[] gregorianMonths; private String[] weekDays; private String cachedCityKey = ""; private CityEntity cachedCity; private Utils(Context context) { this.context = context; prefs = PreferenceManager.getDefaultSharedPreferences(context); updateStoredPreference(); } private static WeakReference<Utils> myWeakInstance; public static Utils getInstance(Context context) { if (myWeakInstance == null || myWeakInstance.get() == null) { myWeakInstance = new WeakReference<>(new Utils(context.getApplicationContext())); } return myWeakInstance.get(); } /** * Text shaping is a essential thing on supporting Arabic script text on older Android versions. * It converts normal Arabic character to their presentation forms according to their position * on the text. * * @param text Arabic string * @return Shaped text */ public String shape(String text) { return (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) ? ArabicShaping.shape(text) : text; } public String programVersion() { try { return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; } catch (NameNotFoundException e) { Log.e(TAG, "Name not found on PersianCalendarUtils.programVersion"); return ""; } } private void initTypeface() { if (typeface == null) { typeface = Typeface.createFromAsset(context.getAssets(), FONT_PATH); } } public void setFont(TextView textView) { initTypeface(); textView.setTypeface(typeface); } public void setFontAndShape(TextView textView) { setFont(textView); textView.setText(shape(textView.getText().toString())); } public void setFontShapeAndGravity(TextView textView) { setFontAndShape(textView); textView.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL); } public void setFontAndShape(PreferenceViewHolder holder) { // See android.support.v7.preference.Preference#onBindViewHolder TextView titleView = (TextView) holder.findViewById(android.R.id.title); if (titleView != null) { setFontAndShape(titleView); } TextView summaryView = (TextView) holder.findViewById(android.R.id.summary); if (summaryView != null) { setFontAndShape(summaryView); } } public void setActivityTitleAndSubtitle(Activity activity, String title, String subtitle) { if (title == null || subtitle == null) { return; } initTypeface(); //noinspection ConstantConditions ActionBar supportActionBar = ((AppCompatActivity) activity).getSupportActionBar(); if (supportActionBar == null) { return; } SpannableString titleSpan = new SpannableString(shape(title)); titleSpan.setSpan(new TypefaceSpan(typeface), 0, titleSpan.length(), 0); titleSpan.setSpan(new RelativeSizeSpan(0.8f), 0, titleSpan.length(), 0); supportActionBar.setTitle(titleSpan); SpannableString subtitleSpan = new SpannableString(shape(subtitle)); subtitleSpan.setSpan(new TypefaceSpan(typeface), 0, subtitleSpan.length(), 0); subtitleSpan.setSpan(new RelativeSizeSpan(0.8f), 0, subtitleSpan.length(), 0); supportActionBar.setSubtitle(subtitleSpan); } public CalculationMethod getCalculationMethod() { // We were using "Jafari" method but later found out Tehran is nearer to time.ir and others // so switched to "Tehran" method as default calculation algorithm return CalculationMethod.valueOf(prefs.getString(PREF_PRAY_TIME_METHOD, DEFAULT_PRAY_TIME_METHOD)); } public int getIslamicOffset() { return Integer.parseInt(prefs.getString( PREF_ISLAMIC_OFFSET, DEFAULT_ISLAMIC_OFFSET).replace("+", "")); } public Coordinate getCoordinate() { CityEntity cityEntity = getCityFromPreference(); if (cityEntity != null) { return cityEntity.getCoordinate(); } try { Coordinate coord = new Coordinate( Double.parseDouble(prefs.getString(PREF_LATITUDE, DEFAULT_LATITUDE)), Double.parseDouble(prefs.getString(PREF_LONGITUDE, DEFAULT_LONGITUDE)), Double.parseDouble(prefs.getString(PREF_ALTITUDE, DEFAULT_ALTITUDE)) ); // If latitude or longitude is zero probably preference is not set yet if (coord.getLatitude() == 0 && coord.getLongitude() == 0) { return null; } return coord; } catch (NumberFormatException e) { return null; } } private char[] preferredDigits; private boolean clockIn24; public boolean iranTime; public void updateStoredPreference() { preferredDigits = isPersianDigitSelected() ? PERSIAN_DIGITS : ARABIC_DIGITS; clockIn24 = prefs.getBoolean(PREF_WIDGET_IN_24, DEFAULT_WIDGET_IN_24); iranTime = prefs.getBoolean(PREF_IRAN_TIME, DEFAULT_IRAN_TIME); } public boolean isPersianDigitSelected() { return prefs.getBoolean(PREF_PERSIAN_DIGITS, DEFAULT_PERSIAN_DIGITS); } public void setTheme(Context context) { String key = prefs.getString(PREF_THEME, ""); int theme = R.style.LightTheme; // default theme if (key.equals(LIGHT_THEME)) { theme = R.style.LightTheme; } else if (key.equals(DARK_THEME)) { theme = R.style.DarkTheme; } context.setTheme(theme); } public boolean isWidgetClock() { return prefs.getBoolean(PREF_WIDGET_CLOCK, DEFAULT_WIDGET_CLOCK); } public boolean isNotifyDate() { return prefs.getBoolean(PREF_NOTIFY_DATE, DEFAULT_NOTIFY_DATE); } public int getAthanVolume() { return prefs.getInt(PREF_ATHAN_VOLUME, DEFAULT_ATHAN_VOLUME); } public String getAppLanguage() { String language = prefs.getString(PREF_APP_LANGUAGE, DEFAULT_APP_LANGUAGE); // If is empty for whatever reason (pref dialog bug, etc), return Persian at least return TextUtils.isEmpty(language) ? DEFAULT_APP_LANGUAGE : language; } public String getTheme() { return prefs.getString(PREF_THEME, LIGHT_THEME); } public String getSelectedWidgetTextColor() { return prefs.getString(PREF_SELECTED_WIDGET_TEXT_COLOR, DEFAULT_SELECTED_WIDGET_TEXT_COLOR); } public PersianDate getToday() { return DateConverter.civilToPersian(new CivilDate(makeCalendarFromDate(new Date()))); } public Calendar makeCalendarFromDate(Date date) { Calendar calendar = Calendar.getInstance(); if (iranTime) { calendar.setTimeZone(TimeZone.getTimeZone("Asia/Tehran")); } calendar.setTime(date); return calendar; } public String clockToString(int hour, int minute) { return formatNumber(String.format(Locale.ENGLISH, "%d:%02d", hour, minute)); } public String getNextOghatTime(Clock clock, boolean changeDate) { Coordinate coordinate = getCoordinate(); if (coordinate != null) { if (prayTimesCalculator == null) { prayTimesCalculator = new PrayTimesCalculator(getCalculationMethod()); changeDate = true; } if (changeDate) { prayTimes = prayTimesCalculator.calculate(new Date(), coordinate); } if (prayTimes.get(PrayTime.FAJR).getInt() > clock.getInt()) { return context.getString(R.string.azan1) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.FAJR)); } else if (prayTimes.get(PrayTime.SUNRISE).getInt() > clock.getInt()) { return context.getString(R.string.aftab1) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.SUNRISE)); } else if (prayTimes.get(PrayTime.DHUHR).getInt() > clock.getInt()) { return context.getString(R.string.azan2) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.DHUHR)); } else if (prayTimes.get(PrayTime.ASR).getInt() > clock.getInt()) { return context.getString(R.string.azan3) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.ASR)); } else if (prayTimes.get(PrayTime.SUNSET).getInt() > clock.getInt()) { return context.getString(R.string.aftab2) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.SUNSET)); } else if (prayTimes.get(PrayTime.MAGHRIB).getInt() > clock.getInt()) { return context.getString(R.string.azan4) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.MAGHRIB)); } else if (prayTimes.get(PrayTime.ISHA).getInt() > clock.getInt()) { return context.getString(R.string.azan5) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.ISHA)); } else if (prayTimes.get(PrayTime.MIDNIGHT).getInt() > clock.getInt()) { return context.getString(R.string.aftab3) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.MIDNIGHT)); } else { return context.getString(R.string.azan1) + ": " + getPersianFormattedClock(prayTimes.get(PrayTime.FAJR)); //this is today & not tomorrow } } else return null; } public String getPersianFormattedClock(Clock clock) { String timeText = null; int hour = clock.getHour(); if (!clockIn24) { if (hour >= 12) { timeText = PM_IN_PERSIAN; hour -= 12; } else { timeText = AM_IN_PERSIAN; } } String result = clockToString(hour, clock.getMinute()); if (!clockIn24) { result = result + " " + timeText; } return result; } public String getPersianFormattedClock(Calendar calendar) { String timeText = null; int hour = calendar.get(Calendar.HOUR_OF_DAY); if (!clockIn24) { if (calendar.get(Calendar.HOUR_OF_DAY) >= 12) { timeText = PM_IN_PERSIAN; hour -= 12; } else { timeText = AM_IN_PERSIAN; } } String result = clockToString(hour, calendar.get(Calendar.MINUTE)); if (!clockIn24) { result = result + " " + timeText; } return result; } public String formatNumber(int number) { return formatNumber(Integer.toString(number)); } public String formatNumber(String number) { if (preferredDigits == ARABIC_DIGITS) return number; char[] result = number.toCharArray(); for (int i = 0; i < result.length; ++i) { char c = number.charAt(i); if (Character.isDigit(c)) result[i] = preferredDigits[Character.getNumericValue(c)]; } return String.valueOf(result); } public String dateToString(AbstractDate date) { return formatNumber(date.getDayOfMonth()) + ' ' + getMonthName(date) + ' ' + formatNumber(date.getYear()); } public String dayTitleSummary(PersianDate persianDate) { return getWeekDayName(persianDate) + PERSIAN_COMMA + " " + dateToString(persianDate); } public String[] monthsNamesOfCalendar(AbstractDate date) { // the next step would be using them so lets check if they have initialized already if (persianMonths == null || gregorianMonths == null || islamicMonths == null) loadLanguageResource(); if (date instanceof PersianDate) return persianMonths.clone(); else if (date instanceof IslamicDate) return islamicMonths.clone(); else return gregorianMonths.clone(); } public String getMonthName(AbstractDate date) { return monthsNamesOfCalendar(date)[date.getMonth() - 1]; } public String getWeekDayName(AbstractDate date) { if (date instanceof IslamicDate) date = DateConverter.islamicToCivil((IslamicDate) date); else if (date instanceof PersianDate) date = DateConverter.persianToCivil((PersianDate) date); if (weekDays == null) loadLanguageResource(); return weekDays[date.getDayOfWeek() % 7]; } public void quickToast(String message) { Toast.makeText(context, shape(message), Toast.LENGTH_SHORT).show(); } public void longToast(String message) { Toast.makeText(context, shape(message), Toast.LENGTH_LONG).show(); } public int getDayIconResource(int day) { try { return preferredDigits == ARABIC_DIGITS ? DAYS_ICONS_AR[day] : DAYS_ICONS[day]; } catch (IndexOutOfBoundsException e) { Log.e(TAG, "No such field is available"); return 0; } } private String readStream(InputStream is) { // http://stackoverflow.com/a/5445161 Scanner s = new Scanner(is).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; } public String readRawResource(@RawRes int res) { return readStream(context.getResources().openRawResource(res)); } private String persianStringToArabic(String text) { return text .replaceAll("ی", "ي") .replaceAll("ک", "ك") .replaceAll("گ", "كی") .replaceAll("ژ", "زی") .replaceAll("چ", "جی") .replaceAll("پ", "بی"); } private <T> Iterable<T> iteratorToIterable(final Iterator<T> iterator) { return new Iterable<T>() { @Override public Iterator<T> iterator() { return iterator; } }; } public List<CityEntity> getAllCities(boolean needsSort) { List<CityEntity> result = new ArrayList<>(); try { JSONObject countries = new JSONObject(readRawResource(R.raw.cities)); for (String countryCode : iteratorToIterable(countries.keys())) { JSONObject country = countries.getJSONObject(countryCode); String countryEn = country.getString("en"); String countryFa = country.getString("fa"); JSONObject cities = country.getJSONObject("cities"); for (String key : iteratorToIterable(cities.keys())) { JSONObject city = cities.getJSONObject(key); String en = city.getString("en"); String fa = city.getString("fa"); Coordinate coordinate = new Coordinate( city.getDouble("latitude"), city.getDouble("longitude"), 0 // city.getDouble("elevation") ); result.add(new CityEntity(key, en, fa, countryCode, countryEn, countryFa, coordinate)); } } } catch (JSONException e) { Log.e(TAG, e.getMessage()); } if (!needsSort) { return result; } final String locale = getAppLanguage(); CityEntity[] cities = result.toArray(new CityEntity[result.size()]); // Sort first by country code then city Arrays.sort(cities, new Comparator<CityEntity>() { @Override public int compare(CityEntity l, CityEntity r) { if (l.getKey().equals("")) { return -1; } if (r.getKey().equals(DEFAULT_CITY)) { return 1; } int compare = r.getCountryCode().compareTo(l.getCountryCode()); if (compare != 0) return compare; if (locale.equals("en")) { return l.getEn().compareTo(r.getEn()); } else { return persianStringToArabic(l.getFa()) .compareTo(persianStringToArabic(r.getFa())); } } }); return Arrays.asList(cities); } private CityEntity getCityFromPreference() { String key = prefs.getString(PREF_SELECTED_LOCATION, ""); if (TextUtils.isEmpty(key) || key.equals(DEFAULT_CITY)) return null; if (key.equals(cachedCityKey)) return cachedCity; // cache last query even if no city available under the key, useful in case invalid // value is somehow inserted on the preference cachedCityKey = key; for (CityEntity cityEntity : getAllCities(false)) if (cityEntity.getKey().equals(key)) return cachedCity = cityEntity; return cachedCity = null; } public String formatCoordinate(Coordinate coordinate, String separator) { return String.format(Locale.getDefault(), "%s: %.4f%s%s: %.4f", context.getString(R.string.latitude), coordinate.getLatitude(), separator, context.getString(R.string.longitude), coordinate.getLongitude()); } public String getCityName(boolean fallbackToCoord) { CityEntity cityEntity = getCityFromPreference(); if (cityEntity != null) return getAppLanguage().equals("en") ? cityEntity.getEn() : cityEntity.getFa(); String geocodedCityName = prefs.getString(PREF_GEOCODED_CITYNAME, ""); if (!TextUtils.isEmpty(geocodedCityName)) return geocodedCityName; if (fallbackToCoord) { Coordinate coordinate = getCoordinate(); if (coordinate != null) return formatCoordinate(coordinate, PERSIAN_COMMA + " "); } return ""; } private void loadEvents() { List<EventEntity> events = new ArrayList<>(); try { JSONArray days = new JSONObject(readRawResource(R.raw.events)).getJSONArray("events"); int length = days.length(); for (int i = 0; i < length; ++i) { JSONObject event = days.getJSONObject(i); int year = event.getInt("year"); int month = event.getInt("month"); int day = event.getInt("day"); String title = event.getString("title"); boolean holiday = event.getBoolean("holiday"); events.add(new EventEntity(new PersianDate(year, month, day), title, holiday)); } } catch (JSONException e) { Log.e(TAG, e.getMessage()); } this.events = events; } private int maxSupportedYear = -1; private int minSupportedYear = -1; private boolean isYearWarnGivenOnce = false; public void checkYearAndWarnIfNeeded(int selectedYear) { // once is enough, see #clearYearWarnFlag() also if (isYearWarnGivenOnce) return; if (maxSupportedYear == -1 || minSupportedYear == -1) loadMinMaxSupportedYear(); if (selectedYear < minSupportedYear) { longToast(context.getString(R.string.holidaysIncompletenessWarning)); isYearWarnGivenOnce = true; } if (selectedYear > maxSupportedYear) { longToast(context.getString(getToday().getYear() > maxSupportedYear ? R.string.shouldBeUpdated : R.string.holidaysIncompletenessWarning)); isYearWarnGivenOnce = true; } } // called from CalendarFragment to make it once per calendar view public void clearYearWarnFlag() { isYearWarnGivenOnce = false; } private void loadMinMaxSupportedYear() { if (events == null) { loadEvents(); } int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; for (EventEntity eventEntity : events) { int year = eventEntity.getDate().getYear(); if (min > year && year != -1) { min = year; } if (max < year) { max = year; } } minSupportedYear = min; maxSupportedYear = max; } public List<EventEntity> getEvents(PersianDate day) { if (events == null) { loadEvents(); } List<EventEntity> result = new ArrayList<>(); for (EventEntity eventEntity : events) { if (eventEntity.getDate().equals(day)) { result.add(eventEntity); } } return result; } public String getEventsTitle(PersianDate day, boolean holiday) { String titles = ""; boolean first = true; List<EventEntity> dayEvents = getEvents(day); for (EventEntity event : dayEvents) { if (event.isHoliday() == holiday) { if (first) { first = false; } else { titles = titles + "\n"; } titles = titles + event.getTitle(); } } return titles; } public void loadApp() { AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Calendar startTime = Calendar.getInstance(); startTime.set(Calendar.HOUR_OF_DAY, 0); startTime.set(Calendar.MINUTE, 1); Intent intent = new Intent(context, BroadcastReceivers.class); intent.setAction(BROADCAST_RESTART_APP); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); alarmManager.set(AlarmManager.RTC, startTime.getTimeInMillis(), pendingIntent); } public boolean isServiceRunning(Class<?> serviceClass) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (serviceClass.getName().equals(service.service.getClassName())) { return true; } } return false; } public String setToCommaSeparated(Set<String> set) { return TextUtils.join(",", set); } public Set<String> commaSeparatedToSet(String commaSeparated) { Set<String> result = new HashSet<>(); result.addAll(Arrays.asList(TextUtils.split(commaSeparated, ","))); return result; } public void loadAlarms() { String prefString = prefs.getString(PREF_ATHAN_ALARM, ""); Log.d(TAG, "reading and loading all alarms from prefs: " + prefString); CalculationMethod calculationMethod = getCalculationMethod(); Coordinate coordinate = getCoordinate(); if (calculationMethod != null && coordinate != null && !TextUtils.isEmpty(prefString)) { PrayTimesCalculator calculator = new PrayTimesCalculator(calculationMethod); Map<PrayTime, Clock> prayTimes = calculator.calculate(new Date(), coordinate); Set<String> alarmTimesSet = commaSeparatedToSet(prefString); // in the past IMSAK was used but now we figured out FAJR was what we wanted if (alarmTimesSet.remove("IMSAK")) { alarmTimesSet.add("FAJR"); } String[] alarmTimesNames = alarmTimesSet.toArray(new String[alarmTimesSet.size()]); for (int i = 0; i < alarmTimesNames.length; i++) { PrayTime prayTime = PrayTime.valueOf(alarmTimesNames[i]); Clock alarmTime = prayTimes.get(prayTime); if (alarmTime != null) { setAlarm(prayTime, alarmTime, i); } } } } public void setAlarm(PrayTime prayTime, Clock clock, int id) { Calendar triggerTime = Calendar.getInstance(); triggerTime.set(Calendar.HOUR_OF_DAY, clock.getHour()); triggerTime.set(Calendar.MINUTE, clock.getMinute()); setAlarm(prayTime, triggerTime.getTimeInMillis(), id); } public void setAlarm(PrayTime prayTime, long timeInMillis, int id) { String valAthanGap = prefs.getString(PREF_ATHAN_GAP, "0"); long athanGap; try { athanGap = (long) (Double.parseDouble(valAthanGap) * 60); } catch (NumberFormatException e) { athanGap = 0; } Calendar triggerTime = Calendar.getInstance(); triggerTime.setTimeInMillis(timeInMillis - TimeUnit.SECONDS.toMillis(athanGap)); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); // don't set an alarm in the past if (!triggerTime.before(Calendar.getInstance())) { Log.d(TAG, "setting alarm for: " + triggerTime.getTime()); Intent intent = new Intent(context, BroadcastReceivers.class); intent.setAction(BROADCAST_ALARM); intent.putExtra(KEY_EXTRA_PRAYER_KEY, prayTime.name()); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, 0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { SetExactAlarm.setExactAlarm(alarmManager, AlarmManager.RTC_WAKEUP, triggerTime.getTimeInMillis(), pendingIntent); } else { alarmManager.set(AlarmManager.RTC_WAKEUP, triggerTime.getTimeInMillis(), pendingIntent); } } } private static class SetExactAlarm { @TargetApi(Build.VERSION_CODES.KITKAT) public static void setExactAlarm(AlarmManager alarmManager, int type, long triggerAtMillis, PendingIntent pendingIntent) { alarmManager.setExact(type, triggerAtMillis, pendingIntent); } } public Uri getAthanUri() { String defaultSoundUri = "android.resource://" + context.getPackageName() + "/" + R.raw.abdulbasit; return Uri.parse(defaultSoundUri); } // Context preferably should be activity context not application public void changeAppLanguage(Context context) { String localeCode = getAppLanguage().replaceAll("-(IR|AF)", ""); Locale locale = new Locale(localeCode); Locale.setDefault(locale); Resources resources = context.getResources(); Configuration config = resources.getConfiguration(); config.locale = locale; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { config.setLayoutDirection(config.locale); } resources.updateConfiguration(config, resources.getDisplayMetrics()); } public void loadLanguageResource() { @RawRes int messagesFile; String lang = getAppLanguage(); if (lang.equals("fa-AF")) messagesFile = R.raw.messages_fa_af; else if (lang.equals("ps")) messagesFile = R.raw.messages_ps; else messagesFile = R.raw.messages_fa; persianMonths = new String[12]; islamicMonths = new String[12]; gregorianMonths = new String[12]; weekDays = new String[7]; try { JSONObject messages = new JSONObject(readRawResource(messagesFile)); JSONArray persianMonthsArray = messages.getJSONArray("PersianCalendarMonths"); for (int i = 0; i < 12; ++i) persianMonths[i] = persianMonthsArray.getString(i); JSONArray islamicMonthsArray = messages.getJSONArray("IslamicCalendarMonths"); for (int i = 0; i < 12; ++i) islamicMonths[i] = islamicMonthsArray.getString(i); JSONArray gregorianMonthsArray = messages.getJSONArray("GregorianCalendarMonths"); for (int i = 0; i < 12; ++i) gregorianMonths[i] = gregorianMonthsArray.getString(i); JSONArray weekDaysArray = messages.getJSONArray("WeekDays"); for (int i = 0; i < 7; ++i) weekDays[i] = weekDaysArray.getString(i); } catch (JSONException e) { Log.e(TAG, e.getMessage()); } } public void copyToClipboard(View view) { // if it is older than this, the view is also shaped which is not good for copying, so just // nvm about backup solution for older Androids if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { CharSequence text = ((TextView) view).getText(); CopyToClipboard.copyToClipboard(text, context); quickToast("«" + text + "»\n" + context.getString(R.string.date_copied_clipboard)); } } private static class CopyToClipboard { @TargetApi(Build.VERSION_CODES.HONEYCOMB) public static void copyToClipboard(CharSequence text, Context context) { ((ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE)) .setPrimaryClip(ClipData.newPlainText("converted date", text)); } } public SeasonEnum getSeason() { int month = getToday().getMonth(); if (month < 4) { return SeasonEnum.SPRING; } else if (month < 7) { return SeasonEnum.SUMMER; } else if (month < 10) { return SeasonEnum.FALL; } else { return SeasonEnum.WINTER; } } public List<DayEntity> getDays(int offset) { List<DayEntity> days = new ArrayList<>(); PersianDate persianDate = getToday(); int month = persianDate.getMonth() - offset; month -= 1; int year = persianDate.getYear(); year = year + (month / 12); month = month % 12; if (month < 0) { year -= 1; month += 12; } month += 1; persianDate.setMonth(month); persianDate.setYear(year); persianDate.setDayOfMonth(1); int dayOfWeek = DateConverter.persianToCivil(persianDate).getDayOfWeek() % 7; try { PersianDate today = getToday(); for (int i = 1; i <= 31; i++) { persianDate.setDayOfMonth(i); DayEntity dayEntity = new DayEntity(); dayEntity.setNum(formatNumber(i)); dayEntity.setDayOfWeek(dayOfWeek); if (dayOfWeek == 6 || !TextUtils.isEmpty(getEventsTitle(persianDate, true))) { dayEntity.setHoliday(true); } if (getEvents(persianDate).size() > 0) { dayEntity.setEvent(true); } dayEntity.setPersianDate(persianDate.clone()); if (persianDate.equals(today)) { dayEntity.setToday(true); } days.add(dayEntity); dayOfWeek++; if (dayOfWeek == 7) { dayOfWeek = 0; } } } catch (DayOutOfRangeException e) { // okay, it was expected } return days; } // based on R.array.calendar_type order public CalendarTypeEnum calendarTypeFromPosition(int position) { if (position == 0) return CalendarTypeEnum.SHAMSI; else if (position == 1) return CalendarTypeEnum.ISLAMIC; else return CalendarTypeEnum.GREGORIAN; } @IdRes public final static int DROPDOWN_LAYOUT = R.layout.select_dialog_item; public int fillYearMonthDaySpinners(Context context, Spinner calendarTypeSpinner, Spinner yearSpinner, Spinner monthSpinner, Spinner daySpinner) { AbstractDate date; PersianDate newDatePersian = getToday(); CivilDate newDateCivil = DateConverter.persianToCivil(newDatePersian); IslamicDate newDateIslamic = DateConverter.persianToIslamic(newDatePersian); date = newDateCivil; switch (calendarTypeFromPosition(calendarTypeSpinner.getSelectedItemPosition())) { case GREGORIAN: date = newDateCivil; break; case ISLAMIC: date = newDateIslamic; break; case SHAMSI: date = newDatePersian; break; } // years spinner init. String[] years = new String[200]; int startingYearOnYearSpinner = date.getYear() - years.length / 2; for (int i = 0; i < years.length; ++i) { years[i] = formatNumber(i + startingYearOnYearSpinner); } yearSpinner.setAdapter(new ShapedArrayAdapter<>(context, DROPDOWN_LAYOUT, years)); yearSpinner.setSelection(years.length / 2); // // month spinner init. String[] months = monthsNamesOfCalendar(date); for (int i = 0; i < months.length; ++i) { months[i] = months[i] + " / " + formatNumber(i + 1); } monthSpinner.setAdapter(new ShapedArrayAdapter<>(context, DROPDOWN_LAYOUT, months)); monthSpinner.setSelection(date.getMonth() - 1); // // days spinner init. String[] days = new String[31]; for (int i = 0; i < days.length; ++i) { days[i] = formatNumber(i + 1); } daySpinner.setAdapter(new ShapedArrayAdapter<>(context, DROPDOWN_LAYOUT, days)); daySpinner.setSelection(date.getDayOfMonth() - 1); // return startingYearOnYearSpinner; } }