com.bt.heliniumstudentapp.ScheduleFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.bt.heliniumstudentapp.ScheduleFragment.java

Source

/*
 *
 * Helinium Studentapp
 *
 * Copyright (C) 2016 Bastiaan Teeuwen <bastiaan.teeuwen170@gmail.com>
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 *
 */

package com.bt.heliniumstudentapp;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ContextThemeWrapper;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.DatePicker;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.IOException;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Scanner;
import java.util.TimeZone;

import static android.support.v4.content.ContextCompat.getColor;

public class ScheduleFragment extends Fragment {
    protected enum extra_i {
        HOMEWORK(0), ABSENCE(1);

        private int i;

        extra_i(int i) {
            this.i = i;
        }

        protected int value() {
            return i;
        }
    }

    /* TODO Seperate */
    protected enum extra_t {
        HOMEWORK, /* "huiswerk" */
        HOMEWORKDONE, /* "huiswerk" + "gemaakt" */
        HOMEWORKLATE, /* "huiswerk" + "gemaakt" */
        TEST, /* "toets" */
        LATE, /* "te laat-T" */
        FULOUGH, /* "Schoolverlof" */
        MEDICAL, /* "Ziek / medisch" */
        TRUANCY, /* "Ongeoorloofd verzuim" */
        REMOVED, /* "verwijderd uit de les" */
        ABSENT /* Other reason for absence */
    }

    /* This . is . just a mess... */
    protected static class week implements Serializable {
        private final day[] days = new day[5];

        private String footer = "";

        //private int year;
        //private int week;

        //private week(final int year, final int week)
        private week() {
            //this.year = year;
            //this.week = week;
        }

        private day day_add(final int n) {
            return new day(n);
        }

        protected day day_get(final int n) {
            return days[n - 2];
        }

        protected int day_get_index(final int n) {
            final ArrayList<Integer> t = new ArrayList<>();
            int i;

            for (i = 0; i < days.length; i++)
                if (days[i] != null)
                    t.add(i);

            return t.get(n);
        }

        protected int days_get() {
            int i, n = 0;

            for (i = 0; i < days.length; i++)
                if (days[i] != null)
                    n++;

            return n;
        }

        private String footer_get() {
            GregorianCalendar today = new GregorianCalendar(HeliniumStudentApp.LOCALE);

            if (scheduleFocus == today.get(Calendar.WEEK_OF_YEAR)
                    || scheduleFocus == today.get(Calendar.WEEK_OF_YEAR) + 1)
                return mainContext.getString(R.string.updated_last) + ' ' + footer + ' '
                        + mainContext.getString(R.string.ago);

            return "";
        }

        protected class day implements Serializable {
            protected final ArrayList<hour> hours = new ArrayList<>();
            private final ArrayList<floating> floatings = new ArrayList<>();

            protected String day, date;
            private boolean today;

            private day(final int n) {
                final GregorianCalendar ld = getDate();
                ld.set(Calendar.DAY_OF_WEEK, n);

                final GregorianCalendar td = new GregorianCalendar(HeliniumStudentApp.LOCALE);
                final String wd = HeliniumStudentApp.df_weekday().format(ld.getTime());

                /* FIXME Inefficient */
                if (ld.get(Calendar.DAY_OF_YEAR) == td.get(Calendar.DAY_OF_YEAR)
                        && ld.get(Calendar.YEAR) == td.get(Calendar.YEAR))
                    today = true;

                this.day = Character.toUpperCase(wd.charAt(0)) + wd.substring(1);
                this.date = DateFormat.getDateFormat(mainContext).format(ld.getTime());

                days[n - 2] = this;
            }

            private hour hour_add(final int n, final String course, final String classroom, final String teacher,
                    final String group) {
                return new hour(n, course, classroom, teacher, group);
            }

            protected hour hour_get(final int n) {
                return hours.get(n);
            }

            protected int hours_get() {
                return hours.size();
            }

            private floating floating_add(final extra_t type, final String course, final String text) {
                return new floating(type, course, text);
            }

            protected floating floating_get(final int n) {
                return floatings.get(n);
            }

            protected int floatings_get() {
                return floatings.size();
            }

            protected class hour implements Serializable {
                /* TODO Confirm 2 is max */
                private extra[] extras = new extra[2];

                protected int hour;
                protected String course;
                protected String classroom;
                protected String teacher;
                protected String group;

                private hour(int n, final String course, final String classroom, final String teacher,
                        final String group) {
                    if (n < 0) {
                        this.hour = n * -1;

                        hours.add(this);

                        return;
                    }

                    if (n > 2) {
                        if (n < 5)
                            n++;
                        else if (n < 7)
                            n += 2;
                        else
                            n += 3;
                    }

                    this.hour = n;
                    this.course = course;
                    this.classroom = classroom;
                    this.teacher = teacher;
                    this.group = group;

                    hours.add(this);
                }

                private extra extra_add(final int n, final extra_t type, final String text) {
                    return new extra(n, type, text);
                }

                protected extra extra_get(final int n) {
                    return extras[n];
                }

                /* This can be any of the types defined in extra_t */
                protected class extra implements Serializable {
                    protected extra_t type;
                    protected String text;

                    private extra(final int n, final extra_t type, final String text) {
                        this.type = type;
                        this.text = text;

                        extras[n] = this;
                    }
                }
            }

            /* This can be either extra_t.HOMEWORK, (extra_t.HOMEWORK_DONE ?) or extra_t.TEST */
            protected class floating implements Serializable {
                protected extra_t type;
                protected String course;
                protected String text;

                private floating(final extra_t type, final String course, final String text) {
                    this.type = type;
                    this.course = course;
                    this.text = text;

                    floatings.add(this);
                }
            }
        }
    }

    private static AppCompatActivity mainContext;
    protected static View scheduleLayout;
    private static boolean init;

    protected static String scheduleJson;
    protected static int scheduleFocus;

    //private static String student_name, student_class;

    private static ListView weekDaysLV;

    @SuppressWarnings("ConstantConditions")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
        mainContext = (AppCompatActivity) getActivity();
        scheduleLayout = inflater.inflate(R.layout.fragment_schedule, viewGroup, false);

        MainActivity.setToolbarTitle(mainContext, getResources().getString(R.string.schedule), null);

        weekDaysLV = (ListView) scheduleLayout.findViewById(R.id.lv_weekDays_fs);

        final boolean restart = PreferenceManager.getDefaultSharedPreferences(mainContext)
                .getBoolean("forced_restart", false);

        if (restart)
            PreferenceManager.getDefaultSharedPreferences(mainContext).edit().putBoolean("forced_restart", false)
                    .apply();

        if (restart) { //TODO Database check?
            MainActivity.setStatusBar(mainContext);

            scheduleFocus = new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.WEEK_OF_YEAR);

            scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext).getString("schedule_0", null);

            if (MainActivity.isOnline())
                parseData(HeliniumStudentApp.ACTION_ONLINE);
            else
                parseData(HeliniumStudentApp.ACTION_OFFLINE);
        } else if (scheduleJson == null) {
            final boolean online = MainActivity.isOnline();

            scheduleFocus = new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.WEEK_OF_YEAR);

            if (online && PreferenceManager.getDefaultSharedPreferences(mainContext)
                    .getBoolean("pref_updates_auto_update", true)) {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
                    new UpdateClass(mainContext, false).execute();
                else
                    new UpdateClass(mainContext, false).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            }

            if (online && (PreferenceManager.getDefaultSharedPreferences(mainContext)
                    .getBoolean("pref_schedule_init", true)
                    || PreferenceManager.getDefaultSharedPreferences(mainContext).getString("schedule_0",
                            null) == null)) {
                getSchedule(HeliniumStudentApp.DIREC_CURRENT, HeliniumStudentApp.ACTION_INIT_IN);
            } else if (checkDatabase() != HeliniumStudentApp.DB_REFRESHING) {
                MainActivity.setStatusBar(mainContext);

                scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext).getString("schedule_0",
                        null);

                if (online)
                    parseData(HeliniumStudentApp.ACTION_ONLINE);
                else
                    parseData(HeliniumStudentApp.ACTION_OFFLINE);
            }
        }

        ((SwipeRefreshLayout) scheduleLayout).setColorSchemeResources(MainActivity.accentSecondaryColor,
                MainActivity.accentPrimaryColor, MainActivity.primaryColor);
        ((SwipeRefreshLayout) scheduleLayout).setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {

            @Override
            public void onRefresh() {
                refresh();
            }
        });

        MainActivity.prevIV.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (checkDatabase() != HeliniumStudentApp.DB_REFRESHING) {
                    if (MainActivity.isOnline()) {
                        scheduleFocus--;

                        getSchedule(HeliniumStudentApp.DIREC_BACK, HeliniumStudentApp.ACTION_REFRESH_IN);
                    } else {
                        final int currentWeek = new GregorianCalendar(HeliniumStudentApp.LOCALE)
                                .get(Calendar.WEEK_OF_YEAR);

                        if (scheduleFocus > currentWeek + 1) {
                            scheduleFocus = currentWeek + 1;
                            scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext)
                                    .getString("schedule_1", null);
                        } else {
                            scheduleFocus = currentWeek;
                            scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext)
                                    .getString("schedule_0", null);
                        }

                        parseData(HeliniumStudentApp.ACTION_OFFLINE);
                    }
                }
            }
        });

        MainActivity.historyIV.setOnClickListener(new OnClickListener() { //FIXME Huge mess
            private int year;
            private int monthOfYear;
            private int dayOfMonth;

            @Override
            public void onClick(View v) {
                if (checkDatabase() != HeliniumStudentApp.DB_REFRESHING) {
                    if (MainActivity.isOnline()) {
                        MainActivity.setUI(HeliniumStudentApp.VIEW_SCHEDULE, HeliniumStudentApp.ACTION_ONLINE);

                        final AlertDialog.Builder weekpickerDialogBuilder = new AlertDialog.Builder(
                                new ContextThemeWrapper(mainContext, MainActivity.themeDialog));

                        final View view = View.inflate(mainContext, R.layout.dialog_schedule, null);
                        weekpickerDialogBuilder.setView(view);

                        final DatePicker datePicker = (DatePicker) view.findViewById(R.id.np_weekpicker_dw);

                        year = new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.YEAR);
                        monthOfYear = new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.MONTH);
                        dayOfMonth = new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.DAY_OF_MONTH);

                        weekpickerDialogBuilder.setTitle(getString(R.string.go_to));

                        weekpickerDialogBuilder.setPositiveButton(getString(android.R.string.ok),
                                new DialogInterface.OnClickListener() {

                                    @Override
                                    public void onClick(DialogInterface dialog, int which) {
                                        final GregorianCalendar date = new GregorianCalendar(
                                                HeliniumStudentApp.LOCALE);
                                        final GregorianCalendar today = new GregorianCalendar(
                                                HeliniumStudentApp.LOCALE);

                                        date.set(Calendar.YEAR, year);
                                        date.set(Calendar.MONTH, monthOfYear);
                                        date.set(Calendar.DAY_OF_MONTH, dayOfMonth);
                                        date.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
                                        today.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

                                        scheduleFocus = (today.get(Calendar.WEEK_OF_YEAR))
                                                - ((int) ((today.getTimeInMillis() / (1000 * 60 * 60 * 24 * 7))
                                                        - (date.getTimeInMillis() / (1000 * 60 * 60 * 24 * 7))));

                                        getSchedule(HeliniumStudentApp.DIREC_OTHER,
                                                HeliniumStudentApp.ACTION_REFRESH_IN);
                                    }
                                });

                        weekpickerDialogBuilder.setNegativeButton(getString(android.R.string.cancel), null);

                        final AlertDialog weekPickerDialog = weekpickerDialogBuilder.create();

                        final GregorianCalendar minDate = new GregorianCalendar(HeliniumStudentApp.LOCALE);
                        minDate.set(Calendar.YEAR, 2000);
                        minDate.set(Calendar.WEEK_OF_YEAR, 1);

                        final GregorianCalendar maxDate = new GregorianCalendar(HeliniumStudentApp.LOCALE);
                        maxDate.set(Calendar.YEAR, 2038);
                        maxDate.set(Calendar.WEEK_OF_YEAR, 1);

                        datePicker.init(year, monthOfYear, dayOfMonth, new DatePicker.OnDateChangedListener() {
                            final GregorianCalendar newDate = new GregorianCalendar(HeliniumStudentApp.LOCALE);

                            @Override
                            public void onDateChanged(DatePicker view, int dialogYear, int dialogMonthOfYear,
                                    int dialogDayOfMonth) {
                                newDate.set(year, monthOfYear, dayOfMonth);

                                year = dialogYear;
                                monthOfYear = dialogMonthOfYear;
                                dayOfMonth = dialogDayOfMonth;

                                if (minDate != null && minDate.after(newDate))
                                    view.init(minDate.get(Calendar.YEAR), minDate.get(Calendar.MONTH),
                                            minDate.get(Calendar.DAY_OF_MONTH), this);
                                else if (maxDate != null && maxDate.before(newDate))
                                    view.init(maxDate.get(Calendar.YEAR), maxDate.get(Calendar.MONTH),
                                            maxDate.get(Calendar.DAY_OF_MONTH), this);
                                else
                                    view.init(year, monthOfYear, dayOfMonth, this);
                            }
                        });

                        weekPickerDialog.setCanceledOnTouchOutside(true);
                        weekPickerDialog.show();

                        weekPickerDialog.getButton(AlertDialog.BUTTON_POSITIVE)
                                .setTextColor(getColor(mainContext, MainActivity.accentSecondaryColor));
                        weekPickerDialog.getButton(AlertDialog.BUTTON_NEGATIVE)
                                .setTextColor(getColor(mainContext, MainActivity.accentSecondaryColor));
                    } else {
                        scheduleFocus = new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.WEEK_OF_YEAR);
                        scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext)
                                .getString("schedule_0", null);

                        parseData(HeliniumStudentApp.ACTION_OFFLINE);
                    }
                }
            }
        });

        MainActivity.nextIV.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                if (checkDatabase() != HeliniumStudentApp.DB_REFRESHING) {
                    if (MainActivity.isOnline()) {
                        scheduleFocus++;

                        getSchedule(HeliniumStudentApp.DIREC_NEXT, HeliniumStudentApp.ACTION_REFRESH_IN);
                    } else {
                        final int currentWeek = new GregorianCalendar(HeliniumStudentApp.LOCALE)
                                .get(Calendar.WEEK_OF_YEAR);

                        if (PreferenceManager.getDefaultSharedPreferences(mainContext).getString("schedule_1",
                                null) != null && scheduleFocus >= currentWeek) {
                            scheduleFocus = currentWeek + 1;
                            scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext)
                                    .getString("schedule_1", null);
                        } else {
                            scheduleFocus = currentWeek;
                            scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext)
                                    .getString("schedule_0", null);
                        }

                        parseData(HeliniumStudentApp.ACTION_OFFLINE);
                    }
                }
            }
        });

        return scheduleLayout;
    }

    @Override
    public void onResume() { //TODO Null check on database needed?
        super.onResume();

        if (init)
            if (MainActivity.isOnline()) {
                if (scheduleJson != null && PreferenceManager.getDefaultSharedPreferences(mainContext)
                        .getString("schedule_0", null) != null
                        && checkDatabase() != HeliniumStudentApp.DB_REFRESHING)
                    parseData(HeliniumStudentApp.ACTION_ONLINE);
            } else {
                if (checkDatabase() != HeliniumStudentApp.DB_REFRESHING) {
                    final int currentWeek = new GregorianCalendar(HeliniumStudentApp.LOCALE)
                            .get(Calendar.WEEK_OF_YEAR);

                    if (scheduleFocus != currentWeek && scheduleFocus != currentWeek + 1) {
                        scheduleFocus = currentWeek;
                        scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext)
                                .getString("schedule_0", null);
                    }

                    parseData(HeliniumStudentApp.ACTION_OFFLINE);
                }
            }
        else
            init = true;
    }

    private void refresh() {
        if (checkDatabase() != HeliniumStudentApp.DB_REFRESHING)
            if (MainActivity.isOnline()) {
                getSchedule(HeliniumStudentApp.DIREC_CURRENT, HeliniumStudentApp.ACTION_REFRESH_IN);
            } else {
                MainActivity.setUI(HeliniumStudentApp.VIEW_SCHEDULE, HeliniumStudentApp.ACTION_REFRESH_OUT);

                if (!MainActivity.displayingSnackbar) {
                    final Snackbar noConnectionSB = Snackbar.make(mainContext.findViewById(R.id.cl_snackbar_am),
                            R.string.error_conn_no, Snackbar.LENGTH_LONG)
                            .setAction(R.string.retry, new OnClickListener() {

                                @Override
                                public void onClick(View v) {
                                    refresh();
                                }
                            });

                    noConnectionSB.getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {

                        @Override
                        public void onViewAttachedToWindow(View v) {
                            MainActivity.displayingSnackbar = true;
                        }

                        @Override
                        public void onViewDetachedFromWindow(View v) {
                            MainActivity.displayingSnackbar = false;
                        }
                    });

                    noConnectionSB.show();
                }

                MainActivity.setUI(HeliniumStudentApp.VIEW_SCHEDULE, HeliniumStudentApp.ACTION_OFFLINE);
            }
    }

    /* FIXME TEMP */
    private static void getSchedule(final int direction, final int transition) {
        MainActivity.setUI(HeliniumStudentApp.VIEW_SCHEDULE, transition);

        try {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
                new GetScheduleData().execute(
                        HeliniumStudentApp.URL_SCHEDULE + String.valueOf(
                                HeliniumStudentApp.df_homework().parse(getDateFormatted() + " GMT").getTime()),
                        scheduleFocus, direction, transition + 1, true);
            else
                new GetScheduleData().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
                        HeliniumStudentApp.URL_SCHEDULE + String.valueOf(
                                HeliniumStudentApp.df_homework().parse(getDateFormatted() + " GMT").getTime()),
                        scheduleFocus, direction, transition + 1, true);
        } catch (ParseException ignored) {
        }
    }

    protected static class GetScheduleData extends AsyncTask<Object, Void, Integer> {
        private String json;

        private String url;
        private int focus;
        private int direction;
        private int transition;
        private boolean display;

        @Override
        protected Integer doInBackground(Object... params) {
            url = (String) params[0];
            focus = (int) params[1];
            direction = (int) params[2];
            transition = (int) params[3];
            display = (boolean) params[4];

            if (MainActivity.cookies == null) {
                return HeliniumStudentApp.ERR_LOGIN;
            } else {
                try {
                    final URLConnection connection = new URL(url).openConnection();

                    connection.setConnectTimeout(HeliniumStudentApp.TIMEOUT_CONNECT);
                    connection.setReadTimeout(HeliniumStudentApp.TIMEOUT_READ);

                    ((HttpURLConnection) connection).setInstanceFollowRedirects(false);
                    connection.setRequestProperty("Accept-Charset", HeliniumStudentApp.CHARSET);
                    connection.setRequestProperty("Content-Type",
                            "application/x-www-form-urlencoded;charset=" + HeliniumStudentApp.CHARSET);
                    connection.addRequestProperty("Cookie",
                            TextUtils.join(",", MainActivity.cookies.getCookieStore().getCookies()));

                    connection.connect();

                    final Scanner line = new Scanner(connection.getInputStream()).useDelimiter("\\A");
                    json = line.hasNext() ? line.next() : "";

                    ((HttpURLConnection) connection).disconnect();

                    final int responseCode = ((HttpURLConnection) connection).getResponseCode();

                    /* XXX Room for improvement here */
                    /*if (json.contains("Er is een fout"))
                    else */if (responseCode == 201 || responseCode == 202)
                        return HeliniumStudentApp.ERR_RETRY;
                    else if (responseCode == 200)
                        //if (json.contains("null")) //TODO Enter
                        //   return HeliniumStudentApp.ERR_UNDEFINED;
                        //else
                        return HeliniumStudentApp.OK;
                    else if (responseCode == 500) //FIXME Not always though
                        return HeliniumStudentApp.ERR_LOGIN;
                    else
                        return HeliniumStudentApp.ERR_OK;
                } catch (IOException e) {
                    return HeliniumStudentApp.ERR_LOGIN;
                }
            }
        }

        @Override
        protected void onPostExecute(Integer returnCode) {
            switch (returnCode) {
            case HeliniumStudentApp.ERR_LOGIN:
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
                    new MainActivity.GetLoginCookie().execute(HeliniumStudentApp.VIEW_SCHEDULE, url, focus,
                            direction, transition, display);
                else
                    new MainActivity.GetLoginCookie().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
                            HeliniumStudentApp.VIEW_SCHEDULE, url, focus, direction, transition, display);
                break;
            case HeliniumStudentApp.ERR_RETRY:
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
                    new GetScheduleData().execute(url, focus, direction, transition, display);
                else
                    new GetScheduleData().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url, focus, direction,
                            transition, display);
                break;
            case HeliniumStudentApp.ERR_OK:
            case HeliniumStudentApp.ERR_UNDEFINED:
                MainActivity.recoverError(HeliniumStudentApp.VIEW_SCHEDULE, returnCode, direction, transition);
                break;
            case HeliniumStudentApp.OK:
                if (display) {
                    scheduleJson = json;
                    parseData(transition);
                }

                if (focus == new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.WEEK_OF_YEAR)) {
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit().putString("schedule_0", json)
                            .apply();
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                            .putString("pref_schedule_version_0", HeliniumStudentApp.df_save().format(new Date()))
                            .apply();
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                            .putString("schedule_start_0", getDateFormatted()).apply();

                    if (PreferenceManager.getDefaultSharedPreferences(mainContext).getBoolean(
                            "pref_schedule_next_week", false) && direction == HeliniumStudentApp.DIREC_CURRENT) {
                        try {
                            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
                                new GetScheduleData().execute(
                                        HeliniumStudentApp.URL_SCHEDULE + String.valueOf(HeliniumStudentApp
                                                .df_homework().parse(getDateFormatted() + " GMT").getTime()),
                                        focus + 1, 0, transition, false);
                            else
                                new GetScheduleData().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
                                        HeliniumStudentApp.URL_SCHEDULE + String.valueOf(HeliniumStudentApp
                                                .df_homework().parse(getDateFormatted() + " GMT").getTime()),
                                        focus + 1, 0, transition, false);
                        } catch (ParseException ignored) {
                        }
                    }
                } else if (focus == new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.WEEK_OF_YEAR) + 1
                        && PreferenceManager.getDefaultSharedPreferences(mainContext)
                                .getBoolean("pref_schedule_next_week", false)) {
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit().putString("schedule_1", json)
                            .apply();
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                            .putString("pref_schedule_version_1", HeliniumStudentApp.df_save().format(new Date()))
                            .apply();
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                            .putString("schedule_start_1", getDateFormatted()).apply();
                }
                break;
            }
        }
    }

    private int checkDatabase() {
        Boolean updated = false;

        final GregorianCalendar currentDate = new GregorianCalendar(HeliniumStudentApp.LOCALE);
        final GregorianCalendar storedDate = new GregorianCalendar(HeliniumStudentApp.LOCALE);

        final String scheduleStart = PreferenceManager.getDefaultSharedPreferences(mainContext)
                .getString("schedule_start_0", null);

        if (scheduleStart == null) {
            return HeliniumStudentApp.DB_OK;
        } else {
            try {
                storedDate.setTime(HeliniumStudentApp.df_date().parse(scheduleStart));
            } catch (ParseException ignored) {
                return HeliniumStudentApp.DB_ERROR;
            }
        }

        for (int weekDays = 0; weekDays < 7; weekDays++) {
            if (currentDate.get(Calendar.YEAR) == storedDate.get(Calendar.YEAR)
                    && currentDate.get(Calendar.MONTH) == storedDate.get(Calendar.MONTH)
                    && currentDate.get(Calendar.DAY_OF_MONTH) == storedDate.get(Calendar.DAY_OF_MONTH)) {
                updated = true;
                break;
            }

            storedDate.add(Calendar.DAY_OF_YEAR, 1);
        }

        if (updated) {
            return HeliniumStudentApp.DB_OK;
        } else {
            scheduleFocus = currentDate.get(Calendar.WEEK_OF_YEAR);

            if (PreferenceManager.getDefaultSharedPreferences(mainContext).getString("schedule_1", null) == null) {
                if (MainActivity.isOnline()) {
                    MainActivity.setStatusBar(mainContext);

                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit().putString("schedule_0", null)
                            .apply();
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit().putString("schedule_1", null)
                            .apply();

                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                            .putString("schedule_start_0", null).apply();
                    PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                            .putString("schedule_start_1", null).apply();

                    ScheduleFragment.getSchedule(HeliniumStudentApp.DIREC_CURRENT,
                            HeliniumStudentApp.ACTION_SHORT_IN);

                    return HeliniumStudentApp.DB_REFRESHING; //TODO Handle by caller to avoid workarounds
                } else {
                    Toast.makeText(mainContext,
                            mainContext.getString(R.string.error_database) + ". "
                                    + mainContext.getString(R.string.error_conn_no) + ".",
                            Toast.LENGTH_SHORT).show();
                    mainContext.finish();

                    return HeliniumStudentApp.DB_ERROR; //TODO Throw error / in finally
                }
            } else
                try {
                    currentDate.setTime(HeliniumStudentApp.df_date().parse(PreferenceManager
                            .getDefaultSharedPreferences(mainContext).getString("schedule_start_0", "1")));

                    if (currentDate.get(Calendar.WEEK_OF_YEAR) - currentDate.get(Calendar.WEEK_OF_YEAR) == 1) {
                        PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                .putString("schedule_0", PreferenceManager.getDefaultSharedPreferences(mainContext)
                                        .getString("schedule_1", null))
                                .apply();
                        PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                .putString("schedule_1", null).apply();

                        PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                .putString("pref_schedule_version_0",
                                        PreferenceManager.getDefaultSharedPreferences(mainContext)
                                                .getString("pref_schedule_version_1", null))
                                .apply();
                        PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                .putString("pref_schedule_version_1", null).apply();

                        PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                .putString("schedule_start_0",
                                        PreferenceManager.getDefaultSharedPreferences(mainContext)
                                                .getString("schedule_start_1", null))
                                .apply();
                        PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                .putString("schedule_start_1", null).apply();

                        scheduleJson = PreferenceManager.getDefaultSharedPreferences(mainContext)
                                .getString("schedule_0", null);

                        return HeliniumStudentApp.DB_OK;
                    } else {
                        if (MainActivity.isOnline()) {
                            MainActivity.setStatusBar(mainContext);

                            PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                    .putString("schedule_0", null).apply();
                            PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                    .putString("schedule_1", null).apply();

                            PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                    .putString("schedule_start_0", null).apply();
                            PreferenceManager.getDefaultSharedPreferences(mainContext).edit()
                                    .putString("schedule_start_1", null).apply();

                            ScheduleFragment.getSchedule(HeliniumStudentApp.DIREC_CURRENT,
                                    HeliniumStudentApp.ACTION_SHORT_IN);

                            return HeliniumStudentApp.DB_REFRESHING; //TODO Handle by caller to avoid workarounds
                        } else {
                            Toast.makeText(mainContext,
                                    mainContext.getString(R.string.error_database) + ". "
                                            + mainContext.getString(R.string.error_conn_no) + ".",
                                    Toast.LENGTH_SHORT).show();
                            mainContext.finish();

                            return HeliniumStudentApp.DB_ERROR; //TODO Throw error / in finally
                        }
                    }
                } catch (ParseException ignored) {
                }

            return HeliniumStudentApp.DB_ERROR; //TODO Throw error / in finally
        }
    }

    private static GregorianCalendar getDate() {
        GregorianCalendar date = new GregorianCalendar(HeliniumStudentApp.LOCALE);
        date.clear();
        date.set(Calendar.YEAR, new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.YEAR)); //FIXME BAD
        date.set(Calendar.WEEK_OF_YEAR, scheduleFocus);
        date.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);

        return date;
    }

    private static String getDateFormatted() {
        return HeliniumStudentApp.df_date().format(getDate().getTime());
    }

    protected static void parseData(final int transition) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
            new ParseData().execute(transition);
        else
            new ParseData().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, transition);
    }

    private static class ParseData extends AsyncTask<Object, Void, week> {
        private AppCompatActivity mainContext;
        private int transition;

        @Override
        protected week doInBackground(final Object... attrs) {
            JSONObject json;
            JSONArray data;
            final week schedule = new week();
            final GregorianCalendar today = new GregorianCalendar(HeliniumStudentApp.LOCALE);
            int i, j = 0, k = -1, l;

            mainContext = ScheduleFragment.mainContext;
            transition = (Integer) attrs[0];

            try {
                json = (JSONObject) new JSONTokener(scheduleJson).nextValue();
                data = json.getJSONArray("events");

                for (i = 0; i < data.length(); i++) {
                    final JSONObject hour_data = data.getJSONObject(i).getJSONObject("afspraakObject");

                    final GregorianCalendar hour_start = new GregorianCalendar(
                            TimeZone.getTimeZone("Europe/Amsterdam"), HeliniumStudentApp.LOCALE);
                    hour_start.setTimeInMillis(data.getJSONObject(i).getLong("start"));
                    final int day = hour_start.get(Calendar.DAY_OF_WEEK);

                    final int hour = hour_data.getInt("lesuur");
                    final String course = data.getJSONObject(i).getString("title");
                    final String classroom = hour_data.getString("lokaal");
                    /* TODO Option to choose between internal and external database */
                    final String teacher = hour_data.getJSONArray("docent").getJSONObject(0).getString("afkorting");
                    final String group = hour_data.getString("lesgroep");

                    week.day day_p = schedule.day_get(day);

                    /* XXX This is a mess..... Clean it up */
                    if (day == k) {
                        for (l = 1; j + l < hour; l++) {
                            switch (j + l - 1) {
                            case 2:
                                day_p.hour_add(-3, null, null, null, null);
                                break;
                            case 4:
                                day_p.hour_add(-6, null, null, null, null);
                                break;
                            case 6:
                                day_p.hour_add(-9, null, null, null, null);
                                break;
                            }

                            day_p.hour_add(j + l, null, null, null, null);
                        }

                        j += l - 1;

                        if (hour > j) {
                            switch (j) {
                            case 2:
                                day_p.hour_add(-3, null, null, null, null);
                                break;
                            case 4:
                                day_p.hour_add(-6, null, null, null, null);
                                break;
                            case 6:
                                day_p.hour_add(-9, null, null, null, null);
                                break;
                            }
                        }
                    }

                    if (day_p == null) {
                        day_p = schedule.day_add(day);
                        k = day;

                        for (l = 1; l < hour; l++)
                            day_p.hour_add(l, null, null, null, null);
                    }

                    final week.day.hour hour_p = day_p.hour_add(hour, course, classroom, teacher, group);

                    if (hour_data.has("huiswerk")) {
                        final JSONObject hw_data = hour_data.getJSONObject("huiswerk");

                        if (hw_data.has("toets") && hw_data.getBoolean("toets")) {
                            hour_p.extra_add(extra_i.HOMEWORK.value(), extra_t.TEST,
                                    hw_data.getString("omschrijving"));
                        } else if (hw_data.has("gemaakt")) {
                            if (hw_data.getBoolean("gemaakt"))
                                hour_p.extra_add(extra_i.HOMEWORK.value(), extra_t.HOMEWORKDONE,
                                        hw_data.getString("omschrijving"));
                            else
                                hour_p.extra_add(extra_i.HOMEWORK.value(), extra_t.HOMEWORKLATE,
                                        hw_data.getString("omschrijving"));
                        } else {
                            hour_p.extra_add(extra_i.HOMEWORK.value(), extra_t.HOMEWORK,
                                    hw_data.getString("omschrijving"));
                        }
                    }

                    if (hour_data.has("absentie")) {
                        final JSONObject ab_data = hour_data.getJSONObject("absentie");

                        if (ab_data.has("reden")) {
                            switch (ab_data.getString("reden")) {
                            case "te laat-T":
                                hour_p.extra_add(extra_i.ABSENCE.value(), extra_t.LATE,
                                        mainContext.getString(R.string.late));
                                break;
                            case "Schoolverlof":
                                hour_p.extra_add(extra_i.ABSENCE.value(), extra_t.FULOUGH,
                                        mainContext.getString(R.string.fulough));
                                break;
                            case "Ziek / medisch":
                                hour_p.extra_add(extra_i.ABSENCE.value(), extra_t.MEDICAL,
                                        mainContext.getString(R.string.medical));
                                break;
                            case "Ongeoorloofd verzuim":
                                hour_p.extra_add(extra_i.ABSENCE.value(), extra_t.TRUANCY,
                                        mainContext.getString(R.string.truancy));
                                break;
                            case "verwijderd uit de les":
                                hour_p.extra_add(extra_i.ABSENCE.value(), extra_t.REMOVED,
                                        mainContext.getString(R.string.removed));
                                break;
                            default:
                                hour_p.extra_add(extra_i.ABSENCE.value(), extra_t.ABSENT,
                                        mainContext.getString(R.string.absent));
                                break;
                            }
                        }
                    }

                    j = hour;
                }
            } catch (JSONException | NullPointerException e) {
                //TODO
                return null;
            }

            try {
                data = json.getJSONArray("huiswerkEvents");

                for (i = 0; i < data.length(); i++) {
                    final JSONObject hw_data = data.getJSONObject(i);

                    final String start = hw_data.getString("start");
                    final GregorianCalendar hour_start = new GregorianCalendar(
                            TimeZone.getTimeZone("Europe/Amsterdam"), HeliniumStudentApp.LOCALE);
                    hour_start.setTime(new SimpleDateFormat("yyyy-MM-dd", HeliniumStudentApp.LOCALE)
                            .parse(start.substring(0, start.indexOf('T'))));
                    final int day = hour_start.get(Calendar.DAY_OF_WEEK);

                    final String course = hw_data.getString("vak");

                    week.day day_p = schedule.day_get(day);
                    if (day_p == null)
                        day_p = schedule.day_add(day);

                    if (hw_data.has("toets") && hw_data.getBoolean("toets"))
                        day_p.floating_add(extra_t.TEST, course, hw_data.getString("omschrijving"));
                    else
                        day_p.floating_add(extra_t.HOMEWORK, course, hw_data.getString("omschrijving"));
                }
            } catch (JSONException | ParseException e) {
                //TODO
                return null;
            }

            /* FIXME Returns wrong results */
            /* TODO Run at interval? */
            /* TODO What about leap years or daylight saving? */

            final SimpleDateFormat format = HeliniumStudentApp.df_save();
            long td;

            try {
                if (scheduleFocus == today.get(Calendar.WEEK_OF_YEAR))
                    td = System.currentTimeMillis()
                            - format.parse(PreferenceManager.getDefaultSharedPreferences(mainContext)
                                    .getString("pref_schedule_version_0", "")).getTime();
                else if (scheduleFocus == today.get(Calendar.WEEK_OF_YEAR) + 1)
                    td = System.currentTimeMillis()
                            - format.parse(PreferenceManager.getDefaultSharedPreferences(mainContext)
                                    .getString("pref_schedule_version_1", "")).getTime();
                else
                    return schedule;

                final long minutes = td / (60 * 1000) % 60;
                final long hours = td / (60 * 60 * 1000) % 24;
                final long days = td / (24 * 60 * 60 * 1000);

                if (days == 0) {
                    if (hours == 0) {
                        if (minutes == 1)
                            schedule.footer = minutes + " " + mainContext.getString(R.string.minute);
                        else
                            schedule.footer = minutes + " " + mainContext.getString(R.string.minutes);
                    } else if (hours == 1) {
                        schedule.footer = hours + " " + mainContext.getString(R.string.hour);
                    } else {
                        schedule.footer = hours + " " + mainContext.getString(R.string.hours);
                    }
                } else {
                    if (days == 1)
                        schedule.footer = days + " " + mainContext.getString(R.string.day);
                    else
                        schedule.footer = days + " " + mainContext.getString(R.string.days);
                }
            } catch (ParseException ignored) {
            }

            return schedule;
        }

        @Override
        protected void onPostExecute(final week schedule) {
            /* TODO Handle */
            if (schedule == null)
                Toast.makeText(mainContext, "The new homework/schedule parser has failed :(", Toast.LENGTH_LONG)
                        .show();

            final ScheduleAdapter scheduleLVadapter = new ScheduleAdapter(schedule);
            weekDaysLV.setAdapter(scheduleLVadapter);

            scheduleLVadapter.notifyDataSetChanged();

            if (scheduleFocus == new GregorianCalendar(HeliniumStudentApp.LOCALE).get(Calendar.WEEK_OF_YEAR))
                MainActivity.weekTV.setTypeface(null, Typeface.BOLD);
            else
                MainActivity.weekTV.setTypeface(null, Typeface.NORMAL);

            final GregorianCalendar date = getDate();
            MainActivity.weekTV.setText(mainContext.getString(R.string.week, date.get(Calendar.WEEK_OF_YEAR)));
            MainActivity.yearTV.setText(String.valueOf(date.get(Calendar.YEAR)));

            /*String nameHtml = scheduleJson; //TODO Implement differently
            nameHtml = nameHtml.substring(nameHtml.indexOf("Rooster van "));
            ((TextView) mainContext.findViewById(R.id.tv_name_hd))
                  .setText((nameHtml.substring(12, nameHtml.indexOf(" ("))));
            ((TextView) mainContext.findViewById(R.id.tv_class_hd))
                  .setText((nameHtml.substring(nameHtml.indexOf("(") + 1, nameHtml.indexOf(")")) +
                  (PreferenceManager.getDefaultSharedPreferences(mainContext)
                  .getString("pref_general_class", "0").equals("0") ? "" :
                  " (" + mainContext.getString(R.string.general_class) + ' ' +
                  PreferenceManager.getDefaultSharedPreferences(mainContext).getString("pref_general_class", "") +
                  ')')));*/
            //((TextView) mainContext.findViewById(R.id.tv_name_hd)).setText("Name unavail.");
            //((TextView) mainContext.findViewById(R.id.tv_class_hd)).setText("Class unavail.");

            weekDaysLV.setOnItemClickListener(new AdapterView.OnItemClickListener() {

                @Override
                public void onItemClick(final AdapterView<?> parent, final View view, final int pos,
                        final long id) {
                    Bundle bundle = new Bundle();
                    bundle.putSerializable("schedule", schedule);
                    bundle.putInt("pos", (int) id);

                    mainContext.startActivity(new Intent(mainContext, DayActivity.class).putExtras(bundle));
                }
            });

            MainActivity.setUI(HeliniumStudentApp.VIEW_SCHEDULE, transition);
        }
    }

    private static class ScheduleAdapter extends BaseAdapter {
        private week schedule;

        public ScheduleAdapter(final week schedule) {
            this.schedule = schedule;
        }

        public int getCount() {
            return schedule.days_get() + 1;
        }

        public Object getItem(int pos) {
            return null;
        }

        public long getItemId(int pos) {
            return pos;
        }

        public View getView(int pos, View convertView, ViewGroup viewGroup) {
            final LayoutInflater inflater = (LayoutInflater) mainContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            week.day day;
            TextView averageTV, dayTV, dateTV;

            if (pos == getCount() - 1) {
                convertView = inflater.inflate(R.layout.listitem_footer, viewGroup, false);
                convertView.setEnabled(false);
                convertView.setOnClickListener(null);

                averageTV = (TextView) convertView.findViewById(R.id.tv_footer_lf);

                averageTV.setTextColor(getColor(mainContext, MainActivity.themeSecondaryTextColor));
                averageTV.setText(schedule.footer_get());

                return convertView;
            }

            convertView = inflater.inflate(R.layout.listitem_schedule, viewGroup, false);

            day = schedule.day_get(schedule.day_get_index(pos) + 2);

            dayTV = (TextView) convertView.findViewById(R.id.tv_day_lm);
            dateTV = (TextView) convertView.findViewById(R.id.tv_date_lm);

            if (MainActivity.themeColor == R.color.theme_dark)
                convertView.setBackgroundResource(R.drawable.listselector_dark);
            else
                convertView.setBackgroundResource(R.drawable.listselector_light);

            if (day.today) {
                dayTV.setTypeface(Typeface.DEFAULT_BOLD);
                dateTV.setTypeface(Typeface.DEFAULT_BOLD);
            }

            dayTV.setTextColor(getColor(mainContext, MainActivity.themePrimaryTextColor));
            dateTV.setTextColor(getColor(mainContext, MainActivity.themeSecondaryTextColor));

            dayTV.setText(day.day);
            dateTV.setText(day.date);

            return convertView;
        }
    }
}