content: package com.alimuzaffar.ramadanalarm.scheduler;
  
  import android.app.AlarmManager;
  import android.app.PendingIntent;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.pm.PackageManager;
  import android.location.LocationManager;
  import android.os.Build;
  import android.support.v4.content.WakefulBroadcastReceiver;
  import android.util.Log;
  
  import com.alimuzaffar.ramadanalarm.BuildConfig;
  import com.alimuzaffar.ramadanalarm.Constants;
  import com.alimuzaffar.ramadanalarm.R;
  import com.alimuzaffar.ramadanalarm.RingAlarmActivity;
  import com.alimuzaffar.ramadanalarm.util.AppSettings;
  import com.alimuzaffar.ramadanalarm.util.PrayTime;
  
  import java.text.DateFormat;
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.LinkedHashMap;
  import java.util.List;
  import java.util.TimeZone;
  
  /**
   * When the alarm fires, this WakefulBroadcastReceiver receives the broadcast Intent
   * and then starts the IntentService {@code SampleSchedulingService} to do some work.
   */
  public class SalaatAlarmReceiver extends WakefulBroadcastReceiver implements Constants {
  
    // The app's AlarmManager, which provides access to the system alarm services.
    private AlarmManager alarmMgr;
    // The pending intent that is triggered when the alarm fires.
    private PendingIntent alarmIntent;
  
    @Override
    public void onReceive(Context context, Intent intent) {
      // BEGIN_INCLUDE(alarm_onreceive)
          /* 
           * If your receiver intent includes extras that need to be passed along to the
           * service, use setComponent() to indicate that the service should handle the
           * receiver's intent. For example:
           * 
           * ComponentName comp = new ComponentName(context.getPackageName(), 
           *      MyService.class.getName());
           *
           * // This intent passed in this call will include the wake lock extra as well as 
           * // the receiver intent contents.
           * startWakefulService(context, (intent.setComponent(comp)));
           * 
           * In this example, we simply create a new intent to deliver to the service.
           * This intent holds an extra identifying the wake lock.
           */
      String prayerName = intent.getStringExtra(EXTRA_PRAYER_NAME);
      long prayerTime = intent.getLongExtra(EXTRA_PRAYER_TIME, -1);
  
      boolean timePassed = (prayerTime != -1 && Math.abs(System.currentTimeMillis() - prayerTime) > FIVE_MINUTES);
  
      AppSettings settings = AppSettings.getInstance(context);
      if (settings.isAlarmSetFor(0)) {
        if (!timePassed) {
          Intent service = new Intent(context, SalaatSchedulingService.class);
          service.putExtra(EXTRA_PRAYER_NAME, prayerName);
  
          // Start the service, keeping the device awake while it is launching.
          startWakefulService(context, service);
          // END_INCLUDE(alarm_onreceive)
  
          // START THE ALARM ACTIVITY
          Intent newIntent = new Intent(context, RingAlarmActivity.class);
          Log.d("SalaatAlarmReceiver", "Alarm Receiver Got " + prayerName);
          newIntent.putExtra(EXTRA_PRAYER_NAME, prayerName);
          newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          context.startActivity(newIntent);
        }
        //SET THE NEXT ALARM
        setAlarm(context);
      }
    }
  
    // BEGIN_INCLUDE(set_alarm)
  
    /**
     * Sets a repeating alarm that runs once a day at approximately 8:30 a.m. When the
     * alarm fires, the app broadcasts an Intent to this WakefulBroadcastReceiver.
     *
     * @param context
     */
    public void setAlarm(Context context) {
      alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
      Intent intent = new Intent(context, SalaatAlarmReceiver.class);
  
      Calendar now = Calendar.getInstance(TimeZone.getDefault());
      now.setTimeInMillis(System.currentTimeMillis());
      // Set the alarm's trigger time to 8:30 a.m.
  
      int alarmIndex = 0;
  
      Calendar then = Calendar.getInstance(TimeZone.getDefault());
      then.setTimeInMillis(System.currentTimeMillis());
  
      AppSettings settings = AppSettings.getInstance(context);
  
      double lat = settings.getLatFor(alarmIndex);
      double lng = settings.getLngFor(alarmIndex);
      LinkedHashMap<String, String> prayerTimes = PrayTime.getPrayerTimes(context, alarmIndex, lat, lng, PrayTime.TIME_24);
      List<String> prayerNames = new ArrayList<>(prayerTimes.keySet());
  
      boolean nextAlarmFound = false;
      String nameOfPrayerFound = null;
      for (String prayer : prayerNames) {
        if (!isAlarmEnabledForPrayer(settings, prayer, alarmIndex)) {
          continue;
        }
  
        then = getCalendarFromPrayerTime(then, prayerTimes.get(prayer));
  
        if (then.after(now)) {
          // this is the alarm to set
          nameOfPrayerFound = prayer;
          nextAlarmFound = true;
          break;
        }
      }
  
      if (!nextAlarmFound) {
        for (String prayer : prayerNames) {
          if (!isAlarmEnabledForPrayer(settings, prayer, alarmIndex)) {
            continue;
          }
  
          then = getCalendarFromPrayerTime(then, prayerTimes.get(prayer));
  
          if (then.before(now)) {
            // this is the next day.
            nameOfPrayerFound = prayer;
            nextAlarmFound = true;
            then.add(Calendar.DAY_OF_YEAR, 1);
            break;
          }
        }
      }
  
      if (!nextAlarmFound) {
        return; //something went wrong, abort!
      }
  
      nameOfPrayerFound = getPrayerNameFromIndex(context, getPrayerIndexFromName(nameOfPrayerFound));
      intent.putExtra(EXTRA_PRAYER_NAME, nameOfPrayerFound);
      intent.putExtra(EXTRA_PRAYER_TIME, then.getTimeInMillis());
      alarmIntent = PendingIntent.getBroadcast(context, ALARM_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
  
      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
        //lollipop_mr1 is 22, this is only 23 and above
        alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, then.getTimeInMillis(), alarmIntent);
      } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
        //JB_MR2 is 18, this is only 19 and above.
        alarmMgr.setExact(AlarmManager.RTC_WAKEUP, then.getTimeInMillis(), alarmIntent);
      } else {
        //available since api1
        alarmMgr.set(AlarmManager.RTC_WAKEUP, then.getTimeInMillis(), alarmIntent);
      }
  
      // SET PASSIVE LOCATION RECEIVER
      Intent passiveIntent = new Intent(context, PassiveLocationChangedReceiver.class);
      PendingIntent locationListenerPassivePendingIntent = PendingIntent.getActivity(context, PASSIVE_LOCATION_ID, passiveIntent, PendingIntent.FLAG_UPDATE_CURRENT);
      requestPassiveLocationUpdates(context, locationListenerPassivePendingIntent);
  
      // Enable {@code SampleBootReceiver} to automatically restart the alarm when the
      // device is rebooted.
      ComponentName receiver = new ComponentName(context, SalaatBootReceiver.class);
      PackageManager pm = context.getPackageManager();
  
      pm.setComponentEnabledSetting(receiver,
          PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
          PackageManager.DONT_KILL_APP);
    }
    // END_INCLUDE(set_alarm)
  
    /**
     * Cancels the alarm.
     *
     * @param context
     */
    // BEGIN_INCLUDE(cancel_alarm)
    public void cancelAlarm(Context context) {
      // If the alarm has been set, cancel it.
      if (alarmMgr == null) {
        alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
      }
      if (alarmMgr != null) {
        if (alarmIntent == null) {
          Intent intent = new Intent(context, SalaatAlarmReceiver.class);
          alarmIntent = PendingIntent.getBroadcast(context, ALARM_ID, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        }
        alarmMgr.cancel(alarmIntent);
  
        //REMOVE PASSIVE LOCATION RECEIVER
        Intent passiveIntent = new Intent(context, PassiveLocationChangedReceiver.class);
        PendingIntent locationListenerPassivePendingIntent = PendingIntent.getActivity(context, PASSIVE_LOCATION_ID, passiveIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        removePassiveLocationUpdates(context, locationListenerPassivePendingIntent);
      }
  
      // Disable {@code SampleBootReceiver} so that it doesn't automatically restart the
      // alarm when the device is rebooted.
      ComponentName receiver = new ComponentName(context, SalaatBootReceiver.class);
      PackageManager pm = context.getPackageManager();
  
      pm.setComponentEnabledSetting(receiver,
          PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
          PackageManager.DONT_KILL_APP);
    }
    // END_INCLUDE(cancel_alarm)
  
  
    public void requestPassiveLocationUpdates(Context context, PendingIntent pendingIntent) {
      long oneHourInMillis = 1000 * 60 * 60;
      long fiftyKinMeters = 50000;
  
      LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
      try {
        locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
            oneHourInMillis, fiftyKinMeters, pendingIntent);
      } catch (SecurityException se) {
        Log.w("SetAlarmReceiver", se.getMessage(), se);
        //do nothing. We should always have permision in order to reach this screen.
      }
    }
  
    public void removePassiveLocationUpdates(Context context, PendingIntent pendingIntent) {
      LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
      try {
        locationManager.removeUpdates(pendingIntent);
      } catch (SecurityException se) {
        //do nothing. We should always have permision in order to reach this screen.
      }
    }
  
    private int getPrayerIndexFromName(String prayerName) {
      String name = prayerName.toLowerCase();
      char index = name.charAt(0);
      switch (index) {
        case 'f':
          return 0;
        case 'd':
          return 1;
        case 'a':
          return 2;
        case 'm':
          return 3;
        case 'i':
          return 4;
      }
      return -1;
    }
  
    private String getPrayerKeyFromIndex(AppSettings settings, int prayerIndex, int index) {
      String key = null;
      switch (prayerIndex) {
        case 0:
          key = settings.getKeyFor(AppSettings.Key.IS_FAJR_ALARM_SET, index);
          break;
        case 1:
          key = settings.getKeyFor(AppSettings.Key.IS_DHUHR_ALARM_SET, index);
          break;
        case 2:
          key = settings.getKeyFor(AppSettings.Key.IS_ASR_ALARM_SET, index);
          break;
        case 3:
          key = settings.getKeyFor(AppSettings.Key.IS_MAGHRIB_ALARM_SET, index);
          break;
        case 4:
          key = settings.getKeyFor(AppSettings.Key.IS_ISHA_ALARM_SET, index);
          break;
      }
      return key;
    }
  
    private String getPrayerNameFromIndex(Context context, int prayerIndex) {
      String prayerName = null;
      switch (prayerIndex) {
        case 0:
          prayerName = context.getString(R.string.fajr);
          break;
        case 1:
          prayerName = context.getString(R.string.dhuhr);
          break;
        case 2:
          prayerName = context.getString(R.string.asr);
          break;
        case 3:
          prayerName = context.getString(R.string.maghrib);
          break;
        case 4:
          prayerName = context.getString(R.string.isha);
          break;
      }
      return prayerName;
    }
  
    private boolean isAlarmEnabledForPrayer(AppSettings settings, String prayer, int alarmIndex) {
      if (prayer.equalsIgnoreCase("sunrise") || prayer.equalsIgnoreCase("sunset")) {
        return false;
      }
  
      int prayerIndex = getPrayerIndexFromName(prayer);
      String key = getPrayerKeyFromIndex(settings, prayerIndex, alarmIndex);
      return settings.getBoolean(key);
    }
  
    private Calendar getCalendarFromPrayerTime(Calendar cal, String prayerTime) {
      String[] time = prayerTime.split(":");
      cal.set(Calendar.HOUR_OF_DAY, Integer.valueOf(time[0]));
      cal.set(Calendar.MINUTE, Integer.valueOf(time[1]));
      cal.set(Calendar.SECOND, 0);
      cal.set(Calendar.MILLISECOND, 0);
      return cal;
    }
  }
  