Android Open Source - SELP2013 Timetable Activity






From Project

Back to project page SELP2013.

License

The source code is released under:

License ======= This work is licensed under the BSD 2-clause license as follows. # BSD 2-clause license Copyright (c) 2013, Sky Welch All rights reserved. Redistribution and use in source and ...

If you think the Android project SELP2013 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

package uk.co.skywelch.selp2013;
/*ww  w .  j  av  a  2s. c  om*/
import java.io.File;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ListView;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class TimetableActivity extends ActionBarActivity implements
    TimetableFragment.OnTimetableItemSelected {
  public static final String TAG = "TimetableActivity";
  public final static String SDRoot = "uk.co.skywelch.selp2013";
  public static ArrayList<Lecture> lectures = new ArrayList<Lecture>();
  public static TimetableListAdapter checked_semesterone_adapter;
  public static TimetableListAdapter checked_semestertwo_adapter;
  public static TimetableListAdapter all_semesterone_adapter;
  public static TimetableListAdapter all_semestertwo_adapter;
  public static CourseListAdapter courses_adapter;
  public static DrawerListAdapter draweradapter;

  public final static List<StringListItem> drawerItems = Arrays.asList(new StringListItem(
      "My timetables", true), new StringListItem("My semester 1", false), new StringListItem(
      "My semester 2", false), new StringListItem("All timetables", true),
      new StringListItem("All semester 1", false),
      new StringListItem("All semester 2", false), new StringListItem("Courses", true),
      new StringListItem("Course list", false));

  public static Set<String> course_checks = new HashSet<String>();
  public static HashMap<String, Course> course_map = new HashMap<String, Course>();
  public static ArrayList<Course> courses = new ArrayList<Course>();
  public static final List<String> days = Arrays.asList("Monday", "Tuesday", "Wednesday",
      "Thursday", "Friday", "Saturday", "Sunday", "Unknown");

  public static HashMap<String, Venue> venue_map = new HashMap<String, Venue>();

  private CharSequence mDrawerTitle;
  private CharSequence mTitle;
  private DrawerLayout mDrawerLayout;
  private View mDrawerView;
  private ListView mDrawerList;
  private ActionBarDrawerToggle mDrawerToggle;

  private MenuItem filesDownloadButton;
  private static boolean coursesDownloading = false;
  private static boolean timetableDownloading = false;
  private static boolean venuesDownloading = false;
  private static boolean coursesParsing = false;
  private static boolean timetableParsing = false;
  private static boolean venuesParsing = false;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Request ability to show progress spinner in the action bar
    supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);

    // Set the appropriate content view
    setContentView(R.layout.activity_home);

    // Set the drawer title to that of the application
    mTitle = mDrawerTitle = getTitle();
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mDrawerList = (ListView) findViewById(R.id.left_drawer);
    mDrawerView = findViewById(R.id.drawer_view);

    draweradapter = new DrawerListAdapter(this, drawerItems);

    // Custom shadow for the navigation drawer
    mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
    mDrawerList.setAdapter(draweradapter);
    mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

    // Set up action bar to act as a navigation drawer
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setHomeButtonEnabled(true);

    mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer,
        R.string.drawer_open, R.string.drawer_close) {
      public void onDrawerClosed(View view) {
        getSupportActionBar().setTitle(mTitle);
      }

      public void onDrawerOpened(View drawerView) {
        getSupportActionBar().setTitle(mDrawerTitle);
      }
    };

    mDrawerLayout.setDrawerListener(mDrawerToggle);

    // Load course check settings
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    String json_checks = preferences.getString("course_checks", "");
    if (!json_checks.equalsIgnoreCase("")) {
      Log.d(TAG, "Course checks: " + json_checks);
      Type checkType = new TypeToken<Set<String>>() {
      }.getType();
      course_checks = new Gson().fromJson(json_checks, checkType);
    } else {
      Log.d(TAG,
          "Course checks was empty in SharedPreferences, not adding any checked courses");
    }

    // Decide which navigation drawer view is selected
    int position = preferences.getInt("selected_menu", -1);
    if (position == -1) {
      // Should select the default semester
      // July - December = semester 1 (position 1)
      // January - June = semester 2 (position 2)

      Calendar c = Calendar.getInstance();
      int month = c.get(Calendar.MONTH);
      if (month >= 1 && month <= 6) {
        position = 2;
      } else {
        position = 1;
      }
    }

    selectItem(position);

    // Set up all the list adapters
    if (courses_adapter == null)
      courses_adapter = new CourseListAdapter(this, R.layout.courses_row,
          TimetableActivity.courses);

    if (checked_semesterone_adapter == null)
      checked_semesterone_adapter = new TimetableListAdapter(this, lectures, 1, true, "");
    if (checked_semestertwo_adapter == null)
      checked_semestertwo_adapter = new TimetableListAdapter(this, lectures, 2, true, "");

    if (all_semesterone_adapter == null)
      all_semesterone_adapter = new TimetableListAdapter(this, lectures, 1, false, "");
    if (all_semestertwo_adapter == null)
      all_semestertwo_adapter = new TimetableListAdapter(this, lectures, 2, false, "");

    // Download and parse files if necessary
    String path = Environment.getExternalStorageDirectory().getPath() + "/"
        + TimetableActivity.SDRoot + "/data/";
    Log.d(TAG, path);
    File SDCardRoot = new File(path);
    File venues_xml = new File(SDCardRoot, "venues.xml");
    File courses_xml = new File(SDCardRoot, "courses.xml");
    File timetable_xml = new File(SDCardRoot, "timetable.xml");

    if (venues_xml.exists() && TimetableActivity.venue_map.isEmpty())
      onVenuesReady(venues_xml);
    else if (!venues_xml.exists()) {
      Log.d(TAG, "Venues XML file doesn't appear to exist. Trying to download it...");
      downloadVenues();
    }

    if (courses_xml.exists() && TimetableActivity.courses.isEmpty())
      onCoursesReady(courses_xml);
    else if (!courses_xml.exists()) {
      Log.d(TAG, "Courses XML file doesn't appear to exist. Trying to download it...");
      downloadCourses();
    }

    if (timetable_xml.exists() && TimetableActivity.lectures.isEmpty())
      onTimetableReady(timetable_xml);
    else if (!timetable_xml.exists()) {
      Log.d(TAG, "Timetable XML file doesn't appear to exist. Trying to download it...");
      downloadTimetable();
    }
  }

  // Navigation drawer item click handler
  private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      selectItem(position);
    }
  }

  @Override
  protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);

    // Make the manual file download button visible
    filesDownloadButton = menu.findItem(R.id.action_files_download);
    filesDownloadButton.setVisible(true);

    return super.onCreateOptionsMenu(menu);
  }

  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    return super.onPrepareOptionsMenu(menu);
  }

  public void downloadCourses() {
    // Make sure we don't spawn two of the same download jobs
    if (!coursesDownloading) {
      FileDownloader coursesDownloader = new FileDownloader() {
        @Override
        protected void onPostExecute(Boolean result) {
          if (result) {
            Log.d(CoursesParseTask.TAG,
                "Courses download successful, calling onCoursesReady");
            onCoursesReady(file);
          } else
            Log.e(CoursesParseTask.TAG,
                "Download reported as failed, not doing onCoursesReady");

          coursesDownloading = false;
          parseDownloadProgress();
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
          Log.d(CoursesParseTask.TAG, "Progress: " + progress[0]);
        }
      };

      coursesDownloading = true;
      parseDownloadProgress();
      coursesDownloader.execute(
          "http://www.inf.ed.ac.uk/teaching/courses/selp/xml/courses.xml", "courses.xml");
    }
  }

  public void downloadTimetable() {
    // Make sure we don't spawn two of the same download jobs
    if (!timetableDownloading) {
      FileDownloader timetableDownloader = new FileDownloader() {
        @Override
        protected void onPostExecute(Boolean result) {
          if (result) {
            Log.d(TimetableParseTask.TAG,
                "Timetable download successful, calling onTimetableReady");
            onTimetableReady(file);
          } else
            Log.e(TimetableParseTask.TAG,
                "Download reported as failed, not calling onTimetableReady");

          timetableDownloading = false;
          parseDownloadProgress();
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
          Log.d(TimetableParseTask.TAG, "Progress: " + progress[0]);
        }
      };

      timetableDownloading = true;
      parseDownloadProgress();
      timetableDownloader.execute(
          "http://www.inf.ed.ac.uk/teaching/courses/selp/xml/timetable.xml",
          "timetable.xml");
    }
  }

  public void downloadVenues() {
    // Make sure we don't spawn two of the same download jobs
    if (!venuesDownloading) {
      FileDownloader venuesDownloader = new FileDownloader() {
        @Override
        protected void onPostExecute(Boolean result) {
          if (result) {
            Log.d(VenuesParseTask.TAG,
                "Venues download successful, calling onVenuesReady");
            onVenuesReady(file);
          } else
            Log.e(VenuesParseTask.TAG,
                "Download reported as failed, not notifying onVenuesReady");

          venuesDownloading = false;
          parseDownloadProgress();
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
          Log.d(VenuesParseTask.TAG, "Progress: " + progress[0]);
        }
      };

      venuesDownloading = true;
      parseDownloadProgress();
      venuesDownloader.execute(
          "http://www.inf.ed.ac.uk/teaching/courses/selp/xml/venues.xml", "venues.xml");
    }
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    // Don't do anything if the navigation drawer handled the menu item
    // click
    if (mDrawerToggle.onOptionsItemSelected(item)) {
      return true;
    }

    // Handle item selection
    switch (item.getItemId()) {
    case R.id.action_about: {
      Intent i = new Intent(this, AboutActivity.class);
      startActivity(i);
      return true;
    }
    case R.id.action_files_download: {
      downloadVenues();
      downloadCourses();
      downloadTimetable();

      return true;
    }
    default:
      return super.onOptionsItemSelected(item);
    }
  }

  // Navigation drawer item selector
  private void selectItem(int position) {
    StringListItem item = drawerItems.get(position);

    // Don't allow selection of section items
    if (item.isSection())
      return;

    // Items 1-5 are timetable specific views
    if (position >= 1 && position <= 5) {
      // Prepare filter options and put them in a bundle to pass to
      // the TimetableFragment
      Fragment fragment = new TimetableFragment();
      Bundle bundle = new Bundle();

      setTitle(item.getText());
      switch (position) {
      case 1:
        bundle.putInt("semester", 1);
        bundle.putBoolean("filter_checked", true);

        break;
      case 2:
        bundle.putInt("semester", 2);
        bundle.putBoolean("filter_checked", true);

        break;

      case 4:
        bundle.putInt("semester", 1);
        bundle.putBoolean("filter_checked", false);

        break;
      case 5:
        bundle.putInt("semester", 2);
        bundle.putBoolean("filter_checked", false);

        break;
      }

      fragment.setArguments(bundle);

      FragmentManager fragmentManager = getSupportFragmentManager();
      fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit();

      SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
      SharedPreferences.Editor editor = preferences.edit();
      editor.putInt("selected_menu", position);
      editor.commit();

      Log.d(TAG, "Saving selected view as " + Integer.toString(position));
    } else if (position == 7) {
      // Item 7 is the course list item
      Intent i = new Intent(this, CourseListActivity.class);
      startActivity(i);
    }

    mDrawerList.setItemChecked(position, true);
    mDrawerLayout.closeDrawer(mDrawerView);
  }

  @Override
  public void setTitle(CharSequence title) {
    mTitle = title;
    getSupportActionBar().setTitle(mTitle);
  }

  @Override
  protected void onPostCreate(Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);

    // Sync the toggle state after onRestoreInstanceState has occurred
    mDrawerToggle.syncState();
  }

  @Override
  public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Pass any configuration change to the drawer toggle
    mDrawerToggle.onConfigurationChanged(newConfig);
  }

  public void onCoursesReady(File file) {
    CoursesParseTask coursesParser = new CoursesParseTask() {
      @Override
      protected void onPostExecute(Boolean result) {
        if (result) {
          // Clear whatever courses we already have
          TimetableActivity.courses.clear();
          // Ready for the new ones
          TimetableActivity.courses.addAll(this.courses);

          // Repopulate the course map
          TimetableActivity.course_map.clear();
          for (int i = 0; i < TimetableActivity.courses.size(); i++) {
            Course course = TimetableActivity.courses.get(i);
            TimetableActivity.course_map.put(course.acronym, course);
          }

          Log.d(TAG,
              "Populated course hashmap with "
                  + Integer.toString(TimetableActivity.course_map.size())
                  + " items");

          TimetableActivity.courses_adapter.notifyDataSetChanged();
        } else
          Log.e(TAG, "Parse reported as failed, not doing course adapter notifications");

        coursesParsing = false;
        parseDownloadProgress();
      }

      @Override
      protected void onProgressUpdate(Integer... progress) {
        Log.d(TAG, "Progress: " + progress[0]);
      }
    };

    coursesParsing = true;
    coursesParser.execute(file);
  }

  // Handles the enabling/disabling of the main progress indicator
  // and the download button
  public void parseDownloadProgress() {
    if (timetableDownloading || coursesDownloading || venuesDownloading || timetableParsing
        || coursesParsing || venuesParsing) {
      setSupportProgressBarIndeterminateVisibility(true);
      if (filesDownloadButton != null)
        filesDownloadButton.setEnabled(false);
    } else {
      setSupportProgressBarIndeterminateVisibility(false);
      if (filesDownloadButton != null)
        filesDownloadButton.setEnabled(true);
    }
  }

  public void onTimetableReady(File file) {
    TimetableParseTask timetableParser = new TimetableParseTask() {
      @Override
      protected void onPostExecute(Boolean result) {
        if (result) {
          // Clear whatever lectures we already have
          TimetableActivity.lectures.clear();
          // Ready for the new ones
          TimetableActivity.lectures.addAll(this.lectures);

          // Check for courses without an expansion and add a dummy
          // item if necessary
          int added_courses = 0;

          for (Lecture l : TimetableActivity.lectures) {
            if (!l.course.isEmpty()
                && !TimetableActivity.course_map.containsKey(l.course)) {
              Course course = new Course("", "", "", "", l.course, false, false,
                  false, false, -1, -1, -1, "", "");
              TimetableActivity.courses.add(course);
              TimetableActivity.course_map.put(l.course, course);
              added_courses++;

              Log.d(TimetableParseTask.TAG, "Course code " + l.course
                  + " was missing. Added dummy item for it.");
            }
          }

          if (added_courses > 0) {
            Log.d(TimetableParseTask.TAG,
                "Found "
                    + Integer.toString(added_courses)
                    + " timetable items without associated courses. Added dummy items for them.");

            Collections.sort(TimetableActivity.courses);
            TimetableActivity.courses_adapter.notifyDataSetChanged();
          }

          resetTimetableAdapters();
        } else
          Log.e(TimetableParseTask.TAG,
              "Parse reported as failed, not doing timetable adapter notifications");

        timetableParsing = false;
        parseDownloadProgress();
      }

      @Override
      protected void onProgressUpdate(Integer... progress) {
        Log.d(TimetableParseTask.TAG, "Progress: " + progress[0]);
      }
    };

    timetableParsing = true;
    timetableParser.execute(file);
  }

  public void onVenuesReady(File file) {
    VenuesParseTask venuesParser = new VenuesParseTask() {
      @Override
      protected void onPostExecute(Boolean result) {
        if (result) {
          // Clear whatever venues we already have
          TimetableActivity.venue_map.clear();

          // Repopulate with the new ones
          for (Venue venue : this.venues) {
            if (!venue.name.isEmpty()) {
              TimetableActivity.venue_map.put(venue.name, venue);
            }
          }

          Log.d(VenuesParseTask.TAG, "Added " + TimetableActivity.venue_map.size()
              + " items to venue map");
        } else
          Log.e(VenuesParseTask.TAG, "Venues parse reported as failed");

        venuesParsing = false;
        parseDownloadProgress();
      }

      @Override
      protected void onProgressUpdate(Integer... progress) {
        Log.d(VenuesParseTask.TAG, "Progress: " + progress[0]);
      }
    };

    venuesParsing = true;
    venuesParser.execute(file);
  }

  public static void resetTimetableAdapters() {
    Log.d(TAG, "Resetting timetable adapters");
    checked_semesterone_adapter.resetItems();
    checked_semestertwo_adapter.resetItems();

    all_semesterone_adapter.resetItems();
    all_semestertwo_adapter.resetItems();
  }

  // Timetable item long press handler
  @Override
  public void onItemLongClicked(final Lecture lecture) {
    // Build and show the information dialog
    // Includes the course and venue option
    String[] info_options = new String[] { "Course (" + lecture.course + ")",
        "Venue (" + lecture.building + ")" };
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle(R.string.dialog_info_title);
    builder.setItems(info_options, new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int which) {
        switch (which) {
        case 0: {
          if (lecture.course.isEmpty()) {
            Log.e(TAG,
                "Didn't get a course code from the long click, button won't do anything.");
          } else {
            Intent i = new Intent(TimetableActivity.this,
                CourseInformationActivity.class);
            i.putExtra(CourseInformationActivity.COURSE_CODE, lecture.course);
            startActivity(i);
          }
          break;
        }
        case 1: {
          if (lecture.building.isEmpty()) {
            Log.e(TAG,
                "Didn't get a venue code from the long click, button won't do anything.");
          } else {
            Intent i = new Intent(TimetableActivity.this,
                VenueInformationActivity.class);
            i.putExtra(VenueInformationActivity.VENUE_CODE, lecture.building);
            startActivity(i);
          }
          break;
        }
        default:
          Log.e(TAG, "Unknown information option selected, not doing anything.");
          break;
        }
      }
    });
    builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int id) {
        Log.d(TAG, "Cancel button clicked, ignoring information options");
      }
    });
    builder.show();
  }
}




Java Source Code List

uk.co.skywelch.selp2013.AboutActivity.java
uk.co.skywelch.selp2013.CourseInformationActivity.java
uk.co.skywelch.selp2013.CourseListActivity.java
uk.co.skywelch.selp2013.CourseListAdapter.java
uk.co.skywelch.selp2013.Course.java
uk.co.skywelch.selp2013.CoursesParseTask.java
uk.co.skywelch.selp2013.CoursesXmlParser.java
uk.co.skywelch.selp2013.DrawerListAdapter.java
uk.co.skywelch.selp2013.FileDownloader.java
uk.co.skywelch.selp2013.HourMinute.java
uk.co.skywelch.selp2013.Lecture.java
uk.co.skywelch.selp2013.ListItem.java
uk.co.skywelch.selp2013.ListSection.java
uk.co.skywelch.selp2013.StringListItem.java
uk.co.skywelch.selp2013.TimetableActivity.java
uk.co.skywelch.selp2013.TimetableFragment.java
uk.co.skywelch.selp2013.TimetableListAdapter.java
uk.co.skywelch.selp2013.TimetableParseTask.java
uk.co.skywelch.selp2013.TimetableXmlParser.java
uk.co.skywelch.selp2013.VenueInformationActivity.java
uk.co.skywelch.selp2013.Venue.java
uk.co.skywelch.selp2013.VenuesParseTask.java
uk.co.skywelch.selp2013.VenuesXmlParser.java
uk.co.skywelch.selp2013.XMLUtilities.java