ch.scythe.hsr.TimeTableActivity.java Source code

Java tutorial

Introduction

Here is the source code for ch.scythe.hsr.TimeTableActivity.java

Source

/* 
 * Copyright (C) 2011 - 2012 Michi Gysel <michael.gysel@gmail.com>
 *
 * This file is part of the HSR Timetable.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */
package ch.scythe.hsr;

import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import ch.scythe.hsr.api.RequestException;
import ch.scythe.hsr.api.TimeTableAPI;
import ch.scythe.hsr.api.ui.UiWeek;
import ch.scythe.hsr.authenticator.AuthenticatorActivity;
import ch.scythe.hsr.enumeration.Weekday;
import ch.scythe.hsr.error.AccessDeniedException;
import ch.scythe.hsr.error.ResponseParseException;
import ch.scythe.hsr.error.ServerConnectionException;
import ch.scythe.hsr.helper.AndroidHelper;
import ch.scythe.hsr.helper.DateHelper;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.viewpagerindicator.TitlePageIndicator;
import com.viewpagerindicator.TitlePageIndicator.IndicatorStyle;

public class TimeTableActivity extends SherlockFragmentActivity {
    // _ViewPager
    public static final int NUM_ITEMS = 6;
    private ViewPager dayPager;
    private MyAdapter fragmentPageAdapter;
    // _UI
    private TextView datebox;
    private TextView weekbox;
    private ProgressDialog progress;
    // _Android
    private AccountManager accountManager;
    private SharedPreferences preferences;
    // _State
    public UiWeek week = new UiWeek();
    public Date lastAcessed;
    private AsyncTask<Object, Integer, UiWeek> task;
    // _Helpers
    private TimeTableAPI api;
    // _Keys
    private static final int DIALOG_NO_USER_PASS = 0;
    private static final int DIALOG_ERROR_FETCH = 1;
    private static final int DIALOG_ERROR_CONNECT = 2;
    private static final int DIALOG_ERROR_PARSE = 3;
    private static final int DIALOG_USER_PASS_FETCH = 4;
    private static final String PREFERENCE_ACTIVATED_TAB_TIMESTAMP = "ActivatedTabTimestamp";
    private static final String PREFERENCE_ACTIVATED_TAB = "ActivatedTab";
    private static final String LOGGING_TAG = "TimeTableActivity";
    private static final String SAVED_INSTANCE_TIMETABLE_WEEK = "TimetableWeek";
    private static final int THRESHOLD_IN_MINUTES = 30;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        api = new TimeTableAPI(TimeTableActivity.this);

        setContentView(R.layout.timetable_main);

        fragmentPageAdapter = new MyAdapter(getSupportFragmentManager());
        accountManager = AccountManager.get(getApplicationContext());
        preferences = getPreferences(MODE_PRIVATE);

        dayPager = (ViewPager) findViewById(R.id.day_pager);
        dayPager.setAdapter(fragmentPageAdapter);

        final float density = getResources().getDisplayMetrics().density;

        TitlePageIndicator titleIndicator = (TitlePageIndicator) findViewById(R.id.titles);
        titleIndicator.setViewPager(dayPager);

        titleIndicator.setBackgroundColor(getResources().getColor(android.R.color.background_dark)); // 0x330065A3
        titleIndicator.setFooterColor(0xFF0065A3);
        titleIndicator.setFooterLineHeight(4 * density); // 1dp
        titleIndicator.setFooterIndicatorHeight(6 * density); // 3dp
        titleIndicator.setFooterIndicatorStyle(IndicatorStyle.Triangle);
        titleIndicator.setTextColor(getResources().getColor(android.R.color.primary_text_dark));
        titleIndicator.setTextColor(getResources().getColor(android.R.color.secondary_text_dark));

        datebox = (TextView) findViewById(R.id.date_value);
        weekbox = (TextView) findViewById(R.id.week_value);

        Date date = new Date();
        weekbox.setText(DateHelper.formatToWeekNumber(date));

        UiWeek lastInstance = (UiWeek) getLastCustomNonConfigurationInstance();
        if (lastInstance != null) {
            Log.i(LOGGING_TAG, "Creating Activity from lastInstance.");
            // there was a screen orientation change.
            // we can don't have to create the ui...
            week = lastInstance;
            datebox.setText(DateHelper.formatToUserFriendlyFormat(week.getLastUpdate()));
        } else if (savedInstanceState != null && savedInstanceState.containsKey(SAVED_INSTANCE_TIMETABLE_WEEK)) {
            Log.i(LOGGING_TAG, "Creating Activity from savedInstanceState.");
            // the state of the app was saved, so we can just update the ui
            week = (UiWeek) savedInstanceState.get(SAVED_INSTANCE_TIMETABLE_WEEK);
            datebox.setText(DateHelper.formatToUserFriendlyFormat(week.getLastUpdate()));
        } else {
            Log.i(LOGGING_TAG, "Creating Activity from scratch.");
            // no data available, read it!
            reloadCurrentTab();
            startRequest(date, false);
        }

    }

    @Override
    protected void onResume() {
        super.onResume();
        reloadCurrentTab();
    }

    @Override
    protected void onPause() {
        super.onPause();
        persistCurrentTab();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (progress != null && progress.isShowing())
            progress.cancel();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putSerializable(SAVED_INSTANCE_TIMETABLE_WEEK, week);
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        return week;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getSupportMenuInflater().inflate(R.menu.mainmenu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.preferences:
            startActivity(new Intent(this, UserPreferencesActivity.class));
            break;
        case R.id.refresh:
            startRequest(new Date(), true);
            break;
        case R.id.about:
            startActivity(new Intent(this, AboutActivity.class));
            break;
        case R.id.today:
            scrollToToday();
            break;
        }
        return true;
    }

    private void persistCurrentTab() {
        SharedPreferences.Editor editor = this.preferences.edit();
        int currentTab = dayPager.getCurrentItem();
        editor.putInt(PREFERENCE_ACTIVATED_TAB, currentTab);
        editor.putLong(PREFERENCE_ACTIVATED_TAB_TIMESTAMP, new Date().getTime());
        Log.d(LOGGING_TAG, "Persisting current tab: " + Weekday.getById(currentTab + 1));
        editor.commit();
    }

    private void reloadCurrentTab() {
        int activatedTab = preferences.getInt(PREFERENCE_ACTIVATED_TAB, -1);
        long activatedTabTimestamp = preferences.getLong(PREFERENCE_ACTIVATED_TAB_TIMESTAMP, -1);

        if (activatedTab == -1 || activatedTabTimestamp == -1) {
            scrollToToday();
        } else {
            long timeDiff = new Date().getTime() - activatedTabTimestamp;

            Log.d(LOGGING_TAG, "Timediff:" + timeDiff + "/" + 1000 * 60 * THRESHOLD_IN_MINUTES);

            if (timeDiff > 1000 * 60 * THRESHOLD_IN_MINUTES) {
                scrollToToday();
            } else {
                dayPager.setCurrentItem(activatedTab);
                Log.d(LOGGING_TAG, "Reloading current tab: " + Weekday.getById(activatedTab + 1));
            }

        }
    }

    private void scrollToToday() {
        Log.d(LOGGING_TAG, "Activating todays tab.");
        Weekday today = Weekday.getByDate(new Date());
        if (today == Weekday.SUNDAY) {
            today = Weekday.MONDAY;
        }
        dayPager.setCurrentItem(today.getId() - 1, true);
        persistCurrentTab();
    }

    private synchronized void startRequest(Date date, boolean forceRequest) {

        Account account = AndroidHelper.getAccount(accountManager);

        persistCurrentTab();

        // try to migrate settings
        if (account == null) {
            SharedPreferences oldPrefs = PreferenceManager.getDefaultSharedPreferences(this);
            String login = oldPrefs.getString(getString(R.string.key_login), null);
            String password = oldPrefs.getString(getString(R.string.key_password), null);

            if (!TextUtils.isEmpty(login) && !TextUtils.isEmpty(password)) {
                account = new Account(login.toLowerCase(), Constants.ACCOUNT_TYPE);
                accountManager.addAccountExplicitly(account, password, null);
                Toast.makeText(getApplicationContext(),
                        "Automatically migrated your HSR Login into the Android AccountManager.", Toast.LENGTH_LONG)
                        .show();
                Editor editor = oldPrefs.edit();
                editor.clear();
                editor.commit();
            }
        }

        if (account == null) {

            showDialog(DIALOG_NO_USER_PASS);
        } else if (api.retrieveRequiresBlockingCall(forceRequest)) { // TODO implement this
            progress = new ProgressDialog(this);
            progress.setMessage(getText(R.string.message_loading_data));
            progress.setIndeterminate(true);
            progress.setCancelable(true);
            progress.setOnCancelListener(new DialogInterface.OnCancelListener() {
                public void onCancel(DialogInterface dialog) {
                    Log.i(LOGGING_TAG, "dialog cancel has been invoked");
                    if (task != null) {
                        task.cancel(true);
                    }
                }
            });
            progress.show();

            // progress = ProgressDialog.show(this, "", getString(R.string.message_loading_data));
            task = new FetchDataTask().execute(date, account, forceRequest);
        } else {
            task = new FetchDataTask().execute(date, account, forceRequest);
        }
    }

    private void updateFragemetsWithData(UiWeek week) {
        for (DayFragment fragment : fragmentPageAdapter.getActiveFragments()) {
            Log.d("TimeTableActivity", "Update Fragment " + fragment.getWeekDay() + " with new data.");
            fragment.updateDate(week);
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        Dialog result;
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        switch (id) {
        case DIALOG_NO_USER_PASS:
            builder.setMessage(getString(R.string.message_configure_credentials)).setCancelable(true)
                    .setPositiveButton(getString(R.string.button_add_login), new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {
                            startActivity(new Intent(TimeTableActivity.this, AuthenticatorActivity.class));
                        }
                    }).setNegativeButton(getString(R.string.button_cancel), null);
            result = builder.create();
            break;
        case DIALOG_ERROR_CONNECT:
            // TODO add option to retry?
            builder.setMessage(getString(R.string.message_error_while_connecting))
                    .setPositiveButton(getString(R.string.button_ok), null);
            result = builder.create();
            break;
        case DIALOG_ERROR_FETCH:
            builder.setMessage(getString(R.string.message_error_while_fetching))
                    .setPositiveButton(getString(R.string.button_ok), null);
            result = builder.create();
            break;
        case DIALOG_ERROR_PARSE:
            builder.setMessage(getString(R.string.message_error_while_parsing))
                    .setPositiveButton(getString(R.string.button_ok), null);
            result = builder.create();
            break;
        case DIALOG_USER_PASS_FETCH:
            builder.setMessage(getString(R.string.message_error_while_fetching_user))
                    .setPositiveButton(getString(R.string.button_ok), null);
            result = builder.create();
            break;
        default:
            result = null;
        }
        return result;
    }

    class FetchDataTask extends AsyncTask<Object, Integer, UiWeek> {

        private Integer errorCode = 0;

        @Override
        protected UiWeek doInBackground(Object... params) {
            Date date = (Date) params[0];
            Account account = (Account) params[1];
            boolean forceRequest = (Boolean) params[2];

            UiWeek result = new UiWeek();

            String password = null;
            try {

                password = accountManager.blockingGetAuthToken(account, Constants.AUTHTOKEN_TYPE, true);

                result = api.retrieve(date, account.name, password, forceRequest);

            } catch (OperationCanceledException e) {
                Log.e(LOGGING_TAG, "Exception while fetching the user from the AccountManager.", e);
                errorCode = DIALOG_USER_PASS_FETCH;
            } catch (AuthenticatorException e) {
                Log.e(LOGGING_TAG, "Exception while fetching the user from the AccountManager.", e);
                errorCode = DIALOG_USER_PASS_FETCH;
            } catch (IOException e) {
                Log.e(LOGGING_TAG, "Exception while fetching the user from the AccountManager.", e);
                errorCode = DIALOG_USER_PASS_FETCH;
            } catch (ResponseParseException e) {
                Log.e(LOGGING_TAG, "Exception while parsing the server response.", e);
                errorCode = DIALOG_ERROR_PARSE;
            } catch (RequestException e) {
                Log.e(LOGGING_TAG, "Exception while fetching data from the server.", e);
                errorCode = DIALOG_ERROR_FETCH;
            } catch (ServerConnectionException e) {
                Log.e(LOGGING_TAG, "Exception while fetching data from the server.", e);
                errorCode = DIALOG_ERROR_CONNECT;
            } catch (AccessDeniedException e) {
                Log.e(LOGGING_TAG, "Exception while fetching data from the server. (AccessDenied!)", e);
                // TODO add better error message
                errorCode = DIALOG_ERROR_FETCH;
            }

            return result;
        }

        @Override
        protected void onPostExecute(UiWeek week) {

            if (errorCode == 0) {
                TimeTableActivity.this.week = week;
                updateFragemetsWithData(week);
                datebox.setText(DateHelper.formatToUserFriendlyFormat(week.getLastUpdate()));
            }

            if (progress != null && progress.isShowing())
                progress.dismiss();

            if (errorCode != 0) {
                showDialog(errorCode);
            } else {
                reloadCurrentTab();
            }

        }

    }

    public class MyAdapter extends FragmentStatePagerAdapter {

        @SuppressLint("UseSparseArrays")
        private final HashMap<Integer, DayFragment> activeFragments = new HashMap<Integer, DayFragment>();

        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return getString(getWeekday(position).getResourceReference());
        }

        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        @Override
        public Fragment getItem(int position) {
            DayFragment fragment = new DayFragment();

            Weekday weekDay = getWeekday(position);

            Bundle args = new Bundle();
            args.putSerializable(DayFragment.FRAGMENT_PARAMETER_DATA, week);
            args.putSerializable(DayFragment.FRAGMENT_PARAMETER_WEEKDAY, weekDay);
            fragment.setArguments(args);

            activeFragments.put(position, fragment);

            return fragment;
        }

        private Weekday getWeekday(int position) {
            return Weekday.getById(position + 1);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            activeFragments.remove(position);
            super.destroyItem(container, position, object);
        }

        public Collection<DayFragment> getActiveFragments() {
            return activeFragments.values();
        }
    }

}