Android Open Source - Link-Schedule Link Schedule






From Project

Back to project page Link-Schedule.

License

The source code is released under:

GNU General Public License

If you think the Android project Link-Schedule listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * Copyright 2011 Kurtis Nusbaum// w w  w  .  j  a va 2  s  . c o  m
 *
 * This file is part of LinkSchedule.  
 *
 * LinkSchedule 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 2 of the 
 * License, or (at your option) any later version.  
 *
 * LinkSchedule 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 LinkSchedule. If not, see 
 * http://www.gnu.org/licenses/.
 */

package org.klnusbaum.linkschedule;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.Log;

import java.util.TreeMap;
import java.util.SortedMap;
import java.util.Map;
import java.util.GregorianCalendar;
import java.util.Calendar;
import java.util.Iterator;

/**
 * Class representing the overall schedule for the link.
 *
 * @author Kurtis Nusbaum
 * @version 1.0
 */
public class LinkSchedule{

  /**
   * Reference to the resources of the application.
   */
  private Resources res;
  /**
   * Instance of the GregorianCalendar that should be used when requesting
   * a new GregorianCalendar. This is mostly here for testing purposes.
   */
  private static GregorianCalendar instanceToUse;
  /**
   * Whether or not a custom instance of the GregorianCalendar has been
   * set and should be used.
   */
  private static boolean useCustomInstance = false;
  /**
   * Last time the LinkSchedule was queried.
   */
  private GregorianCalendar lastQueryDate;
  /**
   * Current snapshots.
   */
  private DaySchedule snapshotYesterday,
    snapshotToday, snapshotTomorrow;
  /**
   * How many forward bus times are included when a snapshot is queired for.
   */
  private static final int SNAPSHOT_NEXT_LENGTH = 8;

  /**
   * The singletonInstance of the LinkSchedule.
   */
  private static LinkSchedule singletonInstance;

  /**
   * Static method used to get the singleton instance of the LinkSchedule
   * 
   * @param res Resources that may be needed is the LinkSchedule hasn't
   * already been constructed.
   */
  public static LinkSchedule getLinkSchedule(Resources res){
    if(singletonInstance == null){
      singletonInstance = new LinkSchedule(res);
    }
    return singletonInstance;
  }

  /**
   * Constructs a new LinkSchedule.
   *
    * @param res Resources for the application. 
   */
  private LinkSchedule(Resources res){
    this.res = res;
  }

  /**
   * Resets the LinkSchedule
   */
  public void reset(){
    lastQueryDate = null;
  }

  /**
   * Sets the custom instance to use when calling getCalendarInstance()
   *
   * @param instance Desired custome instance to use.
   *
   */
  public static void setCustomInstance(Resources res, 
    GregorianCalendar instance)
  {
    instanceToUse = instance;
    useCustomInstance = true;
    getLinkSchedule(res).reset();
  }
    
  /**
   * Gets the GregorianCalendar that should be used as the current time.
   *
    * @return The GregorianCalendar that should be as the current time.
   */
  public static GregorianCalendar getCalendarInstance(){
    if(useCustomInstance){
      return (GregorianCalendar)instanceToUse.clone();
    }
    return (GregorianCalendar)GregorianCalendar.getInstance();
  }

  /**
   * Retrieve the next time a Bus will come for a particular stop.
   *
   * @param busStop The bustop for which the next time is desired.
   * @return A Map.Entry whose value is a string describing the next
   * times a bus will come at the given stop, and whose key is a 
   * GregorianCalendar representing when the next bus will come.
   */
  public Map.Entry getNextTime(String busStop){
    if(busStop == null ||
      busStop.equals(res.getString(R.string.unknown_stop)))
    {
      return null;
    }
    GregorianCalendar currentTime = getCalendarInstance();
    queryPrep(currentTime);
    TreeMap<GregorianCalendar, String> compositeSchedule = 
      getCompositeSchedule(busStop);
    for(Map.Entry me: compositeSchedule.entrySet()){
      if(currentTime.compareTo((GregorianCalendar)me.getKey()) <= 0){
        return me;
      }
    }  
    
    return null;
  }

  /**
    * Gets a snapshot of the times when a bus will arrive at the given stop.
   *
    * @param busStop Bus stop for which the snapshot it desired.
   * @return SortedMap of Length 10 containing the previous bus time, next bus
   * and the next 8 times for the given bus stop.
   */
  public SortedMap<GregorianCalendar, String> getSnapshot(String busStop){
    if(busStop == null ||
      busStop.equals(res.getString(R.string.unknown_stop)))
    {
      return null;
    }

    GregorianCalendar currentTime = getCalendarInstance();
    queryPrep(currentTime);
    TreeMap<GregorianCalendar, String> compositeSchedule = 
      getCompositeSchedule(busStop);
    SortedMap<GregorianCalendar, String> snapshotMap = compositeSchedule.subMap(
      findOneBeforeNext(compositeSchedule, currentTime),
      findSeveralPastNext(compositeSchedule, currentTime, SNAPSHOT_NEXT_LENGTH));
    return snapshotMap;
  }

  /**
   * Get a schedule containing yesterday, today, and tomorrows schedule for a
   * given stop.
   * 
   * @param busStop The Bus stop for which a composite schedule is desired.
   * @return A composite schedule for the given bus stop.
   */
  private TreeMap<GregorianCalendar, String> getCompositeSchedule(
    String busStop)
  {
    TreeMap<GregorianCalendar, String> compositeSchedule = 
      new TreeMap<GregorianCalendar, String>();
    compositeSchedule.putAll(snapshotYesterday.getBusStopSched(busStop));
    compositeSchedule.putAll(snapshotToday.getBusStopSched(busStop));
    compositeSchedule.putAll(snapshotTomorrow.getBusStopSched(busStop));
    return compositeSchedule;
  }

  /**
   * Find the key occurs after a certain number of bus times from the current
   * time.
   *
    * @param compositeSchedule The schedule to search for the times.
   * @param currentTime What the current time is.
   * @param numberPast How far past the current time to go.
   * @return A key that points in compositeSchedule numberPast times after the
   * current time.
   */
  private GregorianCalendar findSeveralPastNext(
    SortedMap<GregorianCalendar, String> compositeSchedule, 
    GregorianCalendar currentTime,
    int numberPast)
  {
    Iterator<GregorianCalendar> itr = compositeSchedule.keySet().iterator();
    while(itr.hasNext() && itr.next().compareTo(currentTime) < 0){}
    for(int i=0; i<numberPast && itr.hasNext(); i++){
      itr.next();
    }    
    return itr.next();
  }

  /**
   * Find the time that occurs before the next bus time.
   *
    * @param compositeSchedule The schedule to search.
   * @param currentTime The current next time.
   * @return The time that occurs before the next bus time.
   */
  private GregorianCalendar findOneBeforeNext(
    SortedMap<GregorianCalendar, String> compositeSchedule, 
    GregorianCalendar currentTime)
  {
    Iterator<GregorianCalendar> itr = compositeSchedule.keySet().iterator();
    GregorianCalendar previous = itr.next();
    GregorianCalendar current = itr.next();
    while(itr.hasNext() && current.compareTo(currentTime) < 0){
      previous = current;
      current = itr.next();
    }
    return previous;
  }

  /**
   * Make sure the LinkSchedule is preped and ready to be queried.
    *
    * @param currentTime The current time of day. 
   */
  private void queryPrep(GregorianCalendar currentTime){
    if(lastQueryDate == null || 
      !((isMidWeek(lastQueryDate) && isMidWeek(currentTime)) || 
      sameDayOfWeek(lastQueryDate, currentTime)))
    {
      if(isMonday(currentTime)){
        snapshotYesterday = getWeekendSchedule();
        snapshotToday = getDailySchedule();
        snapshotTomorrow = getDailySchedule();
      }
      else if(isFriday(currentTime)){
        snapshotYesterday = getDailySchedule();
        snapshotToday = getDailySchedule();
        snapshotTomorrow = getWeekendSchedule();
      }
      else if(isWeekday(currentTime)){
        snapshotYesterday = getDailySchedule();
        snapshotToday = getDailySchedule();
        snapshotTomorrow = getDailySchedule();
      }
      else if(isSaturday(currentTime)){
        snapshotYesterday = getDailySchedule();
        snapshotToday = getWeekendSchedule();
        snapshotTomorrow = getWeekendSchedule();
      }
      else if(isSunday(currentTime)){
        snapshotYesterday = getWeekendSchedule();
        snapshotToday = getWeekendSchedule();
        snapshotTomorrow = getDailySchedule();
      }
      snapshotYesterday.dayDecrement();
      snapshotTomorrow.dayIncrement();
    }
    lastQueryDate = currentTime;  
  }
    
  /**
   * Get the standard schedule for the weekend.
   *
   * @return The standard schedule for the weekend.
   */
  public DaySchedule getWeekendSchedule(){
    TreeMap<GregorianCalendar, String> goreckiMap = getSimpleCalendars(
      getCalendarFromString(res.getString(R.string.gorecki_weekend_morning_start)),
      getCalendarFromString(res.getString(R.string.gorecki_weekend_morning_end)),
      getCalendarFromString(res.getString(R.string.gorecki_weekend_day_start)),
      getCalendarFromString(res.getString(R.string.gorecki_weekend_day_end)),
      getCalendarFromString(res.getString(R.string.gorecki_night_start)),
      getCalendarFromString(res.getString(R.string.gorecki_night_end)),
      0
    );

    TreeMap<GregorianCalendar, String> sextonMap = getSimpleCalendars(
      getCalendarFromString(res.getString(R.string.sexton_weekend_morning_start)),
      getCalendarFromString(res.getString(R.string.sexton_weekend_morning_end)),
      getCalendarFromString(res.getString(R.string.sexton_weekend_day_start)),
      getCalendarFromString(res.getString(R.string.sexton_weekend_day_end)),
      getCalendarFromString(res.getString(R.string.sexton_night_start)),
      getCalendarFromString(res.getString(R.string.sexton_night_end)),
      0
    );

    TreeMap<GregorianCalendar, String> flynntownMap = getSimpleCalendars(
      getCalendarFromString(res.getString(R.string.sexton_weekend_morning_start)),
      getCalendarFromString(res.getString(R.string.sexton_weekend_morning_end)),
      getCalendarFromString(res.getString(R.string.sexton_weekend_day_start)),
      getCalendarFromString(res.getString(R.string.sexton_weekend_day_end)),
      getCalendarFromString(res.getString(R.string.sexton_night_start)),
      getCalendarFromString(res.getString(R.string.sexton_night_end)),
      res.getInteger(R.integer.flynntown_offset)
    );

    TreeMap<GregorianCalendar, String> hccMap = getSimpleCalendars(
      getCalendarFromString(res.getString(R.string.gorecki_weekend_morning_start)),
      getCalendarFromString(res.getString(R.string.gorecki_weekend_morning_end)),
      getCalendarFromString(res.getString(R.string.gorecki_weekend_day_start)),
      getCalendarFromString(res.getString(R.string.gorecki_weekend_day_end)),
      getCalendarFromString(res.getString(R.string.gorecki_night_start)),
      getCalendarFromString(res.getString(R.string.gorecki_night_end)),
      0
    );
    return new DaySchedule(flynntownMap, goreckiMap, hccMap, sextonMap, res);
  }

  /**
    * Get the standard schedule for the week.
   * 
   * @return The standard schedule for the week.
   */
  public DaySchedule getDailySchedule(){
    TreeMap<GregorianCalendar, String> goreckiMap = getCalendarsWithLabels(
      res.getStringArray(R.array.gorecki_weekday_times),
      res.getStringArray(R.array.gorecki_weekday_labels),
      getCalendarFromString(res.getString(R.string.gorecki_night_start)),
      getCalendarFromString(res.getString(R.string.gorecki_night_end))
    );

    TreeMap<GregorianCalendar, String> sextonMap = getCalendarsWithLabels(
      res.getStringArray(R.array.sexton_weekday_times),
      res.getStringArray(R.array.sexton_weekday_labels),
      getCalendarFromString(res.getString(R.string.sexton_night_start)),
      getCalendarFromString(res.getString(R.string.sexton_night_end))
    );

    TreeMap<GregorianCalendar, String> flynntownMap = getFixedDayCalendars(
      res.getStringArray(R.array.flynntown_weekday_times),
      getCalendarFromString(res.getString(R.string.sexton_night_start)),
      getCalendarFromString(res.getString(R.string.sexton_night_end)),
      res.getInteger(R.integer.flynntown_offset)
    );

    TreeMap<GregorianCalendar, String> hccMap = getFixedDayCalendars(
      res.getStringArray(R.array.hcc_weekday_times),
      getCalendarFromString(res.getString(R.string.gorecki_night_start)),
      getCalendarFromString(res.getString(R.string.gorecki_night_end)),
      0
    );
    return new DaySchedule(flynntownMap, goreckiMap, hccMap, sextonMap, res);
  }

  /**
   * Get a simple Map of calendars and strings.
   *
    * @param morningStart When the morning bus starts.
   * @param morningEnd When the morning bus ends.
   * @param dayStart When the day bus starts.
   * @param dayEnd When the day bus ends.
   * @param nightStart When the night bus starts.
   * @param nightEnd When the night bus ends.
   * @param offset Any offset that should be applied to the calculated times.
   * 
   * @return A simple map of calendars and strings.
   */
  private TreeMap<GregorianCalendar, String> getSimpleCalendars(
    GregorianCalendar morningStart,
    GregorianCalendar morningEnd,
    GregorianCalendar dayStart, 
    GregorianCalendar dayEnd,
    GregorianCalendar nightStart, 
    GregorianCalendar nightEnd,
    int offset)
  {
    TreeMap<GregorianCalendar, String> toReturn = 
      new TreeMap<GregorianCalendar, String>();
    iterateInsert(toReturn, morningStart, morningEnd, offset);
    iterateInsert(toReturn, dayStart, dayEnd, offset);
    iterateInsert(toReturn, nightStart, nightEnd, offset);
    return toReturn;
  }

  /**
    * Get a Map of calendars and strings that have fixed times.
   *
   * @param dayTimes The times/lables for each time during the day.
    * @param nightStart The start of the night bus times.
   * @param nightEnd The end of the night bus times.
   * @param offset Any offset that should be applied to the calculated times.
   * @return A map of calendars and strings that have fixed times.
   */
  private TreeMap<GregorianCalendar, String> getFixedDayCalendars(
    String[] dayTimes, 
    GregorianCalendar nightStart, 
    GregorianCalendar nightEnd,
    int offset)
  {
    TreeMap<GregorianCalendar, String> toReturn = 
      new TreeMap<GregorianCalendar, String>();
    String currentTimeString;
    for(int i=0; i<dayTimes.length; i++){
      GregorianCalendar toInsert = getCalendarFromString(dayTimes[i]);
      toReturn.put(toInsert, getStandardLabel(toInsert));
    }
    iterateInsert(toReturn, nightStart, nightEnd, offset);
    return toReturn;
  }

  /**
    * Get a Map of calendars and strings that have fixed times and particular
   * labels.
   *
   * @param dayTimes The times for each time during the day.
   * @param dayLabels The labels for each time during the day.
    * @param nightStart The start of the night bus times.
   * @param nightEnd The end of the night bus times.
   * @param offset Any offset that should be applied to the calculated times.
   * @return A map of calendars and strings that have fixed times.
   */
  private TreeMap<GregorianCalendar, String> getCalendarsWithLabels(
    String[] dayTimes, 
    String[] dayLabels, 
    GregorianCalendar nightStart, 
    GregorianCalendar nightEnd)
  {
    TreeMap<GregorianCalendar, String> toReturn = 
      new TreeMap<GregorianCalendar, String>();
    for(int i=0; i<dayTimes.length; i++){
      toReturn.put(
        getCalendarFromString(dayTimes[i]), 
        dayLabels[i]);
    }
    iterateInsert(toReturn, nightStart, nightEnd, 0);
    return toReturn;
  }

  /**
    * Insert a set of GregorianCalendar-String pairs into the given map.
   * 
   * @param map Map in which the paris should be inserted.
   * @param start The starting GregorianCalendar.
   * @param end The ending GregorianCalendar.
   * @param offset The offset that should be applied to all calculated times.
   */
  private void iterateInsert(
    TreeMap<GregorianCalendar, String> map, 
    GregorianCalendar start, 
    GregorianCalendar end,
    int offset)
  {
    GregorianCalendar toInsert = (GregorianCalendar)start.clone();
    toInsert.add(Calendar.MINUTE, offset);
    GregorianCalendar lastTime = (GregorianCalendar)end.clone();
    lastTime.add(Calendar.MINUTE, offset);
    do{
      map.put(toInsert, getStandardLabel(toInsert));
      toInsert = (GregorianCalendar)toInsert.clone();
      toInsert.add(Calendar.MINUTE, 
        res.getInteger(R.integer.standard_bus_interval));
    }while(toInsert.compareTo(lastTime) <= 0);
  }

  /**
    * Create a GregorianCalendar from a string.
   * 
   * @param timeString The string from which the GregorianCalendar should be
   * created.
   * @return The created GregorianCalendar
   */
  public static GregorianCalendar getCalendarFromString(String timeString){
    GregorianCalendar toReturn = getCalendarInstance();
    String toks1[] = timeString.split(":");
    toReturn.set(Calendar.HOUR_OF_DAY, Integer.parseInt(toks1[0]));
    toReturn.set(Calendar.MINUTE, Integer.parseInt(toks1[1]));
    return toReturn;
  }

  /**
   * Get a standard label for a given GregorianCalendar.
   * 
   * @param calendar The calendar for which a label should be created.
   * @return The label for the given calendar.
   */
  public static String getStandardLabel(GregorianCalendar calendar){
    String toReturn = "";
    String minute = String.valueOf(calendar.get(Calendar.MINUTE));
    if(minute.length() == 1){
      minute = "0" + minute;
    }
    String hour = String.valueOf(calendar.get(Calendar.HOUR));
    if(hour.equals("0")){
      hour = "12";
    }
    toReturn = hour + ":" + minute + " " + 
      (calendar.get(Calendar.AM_PM) == Calendar.AM ? "a.m." : "p.m.");
    return toReturn;
  }

  /**
   * Test if a given GregorianCalendar is a weekday.
   *
   * @param date GregorianCalendar to test.
   * @return True if the GregorianCalendar is a weekday, false otherwise.
   */
  public static boolean isWeekday(GregorianCalendar date){
    return !(date.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY)
      && !(date.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY);
  }

  /**
   * Test if a given GregorianCalendar is a friday.
   *
   * @param date GregorianCalendar to test.
   * @return True if the GregorianCalendar is a friday, false otherwise.
   */
  public static boolean isFriday(GregorianCalendar date){
    return date.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY; 
  }

  /**
   * Test if a given GregorianCalendar is a monday.
   *
   * @param date GregorianCalendar to test.
   * @return True if the GregorianCalendar is a monday, false otherwise.
   */
  public static boolean isMonday(GregorianCalendar date){
    return date.get(Calendar.DAY_OF_WEEK) == Calendar.MONDAY; 
  }

  /**
   * Test if a given GregorianCalendar is a sunday.
   *
   * @param date GregorianCalendar to test.
   * @return True if the GregorianCalendar is a sunday, false otherwise.
   */
  public static boolean isSunday(GregorianCalendar date){
    return date.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY; 
  }

  /**
   * Test if a given GregorianCalendar is a saturday.
   *
   * @param date GregorianCalendar to test.
   * @return True if the GregorianCalendar is a saturday, false otherwise.
   */
  public static boolean isSaturday(GregorianCalendar date){
    return date.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY; 
  }

  /**
   * Test if a given GregorianCalendar is in midweek.
   *
   * @param date GregorianCalendar to test.
   * @return True if the GregorianCalendar is in midweek, false otherwise.
   */
  public static boolean isMidWeek(GregorianCalendar date){
    return date.get(Calendar.DAY_OF_WEEK) == Calendar.TUESDAY
      || date.get(Calendar.DAY_OF_WEEK) == Calendar.WEDNESDAY
      || date.get(Calendar.DAY_OF_WEEK) == Calendar.THURSDAY;
  }

  /**
   * Test if two calendars are the same day of the week.
   *
   * @param date1 First GregorianCalendar to compare.
   * @param date2 Second GregorianCalendar to compare.
   * @return True if the dates are the same day of the week, false otherwise.
   */
  public static boolean sameDayOfWeek(GregorianCalendar date1, 
    GregorianCalendar date2)
  {
    return date1.get(Calendar.DAY_OF_WEEK) == date2.get(Calendar.DAY_OF_WEEK);
  }

  /**
   * Determines whether or not the given bus stop is a valid bus stop.
   * @param busStop Bus stop whose validity is to be determined.
   * @param context The context within the test is occuring.
    *
   * @return True if the given bus stop is valid, false otherwise.
   */
  public static boolean validBusStop(String busStop, Context context){
    return busStop.equals(context.getString(R.string.flynntown_name)) ||
      busStop.equals(context.getString(R.string.gorecki_name)) ||  
      busStop.equals(context.getString(R.string.hcc_name)) ||  
      busStop.equals(context.getString(R.string.sexton_name));
  }

}




Java Source Code List

org.klnusbaum.linkschedule.AlarmReceiver.java
org.klnusbaum.linkschedule.BusStopActivity.java
org.klnusbaum.linkschedule.BusStopWidgetConfigure.java
org.klnusbaum.linkschedule.BusStopWidgetProvider.java
org.klnusbaum.linkschedule.CalendarBackedView.java
org.klnusbaum.linkschedule.ClockView.java
org.klnusbaum.linkschedule.DaySchedule.java
org.klnusbaum.linkschedule.LinkSchedule.java
org.klnusbaum.linkschedule.OmniScheduleActivity.java
org.klnusbaum.linkschedule.Refreshable.java
org.klnusbaum.linkschedule.SingleStopActivity.java
org.klnusbaum.linkschedule.StopTimeView.java
org.klnusbaum.linkschedule.TimeChangeReceiver.java
org.klnusbaum.linkschedule.TimeTransform.java
org.klnusbaum.linkschedule.XMLPreferencesActivity.java