net.willwebberley.gowertides.ui.DaysActivity.java Source code

Java tutorial

Introduction

Here is the source code for net.willwebberley.gowertides.ui.DaysActivity.java

Source

/*
Copyright 2013 Will Webberley.
    
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
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.
    
The full text of the License is available in the root of this
project repository.
*/

package net.willwebberley.gowertides.ui;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

import android.net.Uri;
import net.willwebberley.gowertides.R;
import net.willwebberley.gowertides.classes.*;
import net.willwebberley.gowertides.utils.DayDatabase;
import net.willwebberley.gowertides.utils.Utilities;
import net.willwebberley.gowertides.utils.WeatherDatabase;

import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ScrollView;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;

/*
 * Main Activity of application
 *
 * This class maintains the ViewPager of days as well as indicating the currently selected day and handles some button
  * press events.
  *
  * ViewPager contains a list of fragments, each representing a day.
  *
  * This class ia also responsible for network tasks (getting weather), and communicating this to the day fragments.
  *
 */
public class DaysActivity extends FragmentActivity {

    private SharedPreferences prefs;
    private ViewPager infoPager;
    private int todayFragmentIndex;
    private int currentFragmentIndex;
    private Calendar currentDay, firstDay, lastDay;
    private Vector<DayFragment> fragments;
    private PagerAdapter mPagerAdapter;
    public DayDatabase db;
    public WeatherDatabase weather_db;
    public Boolean isPaused, isSyncing;
    public SimpleDateFormat dayDateFormatter;

    private final int DAYS_TO_STORE = 40;

    private TextView dateText;
    private ImageView revertButton;
    private RelativeLayout buildProgressHolder;
    private ProgressBar buildProgress;
    private ImageView refreshButton;
    private ProgressBar refreshProgress;

    public String[] locationNames;
    public int[] locationKeys;
    public int locationIndex;

    private int pauseCounter = 0;

    /*
    * onCreate() called upon activity start.
    *
    * Responsible for initializing major components, loading app preferences and initializing some global variables.
    * Creates the viewpager object and assigns an adapter.
    *
    * Starts the StartupTasks thread to handle long-running startup tasks (while progress bar shows on UI).
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dayview);

        /*
        * Following two variables used by day fragments to check the status of parent activity
         */
        isPaused = false;
        isSyncing = false;

        prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

        initComponents();
        fragments = new Vector<DayFragment>();
        mPagerAdapter = new PagerAdapter(super.getSupportFragmentManager(), fragments);
        infoPager.setAdapter(mPagerAdapter);

        new StartupTasks().execute("");

    }

    /*
    * Called from StartupTasks thread and is used to populate the viewpager with the day fragments.
    *
    * Creates DAYS_TO_STORE number of day fragments equally distributed around the current day and calculates and stores
    * the index of the current day of the list.
     */
    private void populatePager(Calendar newToday, int daysToLoad) {
        fragments.clear();
        Calendar startDay = (Calendar) newToday.clone();
        // Change the day to start the pager at (e.g., if 4, will start at day - DAYS_TO_STORE/4 and end at
        // 3*DAYS_TO_STORE/4.
        startDay.add(Calendar.DATE, -(daysToLoad / 4));
        for (int i = 0; i < DAYS_TO_STORE; i++) {
            Calendar newDay = (Calendar) startDay.clone();
            newDay.add(Calendar.DATE, i);

            Day day = Utilities.createDay(db, weather_db, newDay, locationKeys[locationIndex]);
            DayFragment dayFrag = new DayFragment(day, prefs, this);

            if (currentDay.getTimeInMillis() == newDay.getTimeInMillis()) {
                todayFragmentIndex = i;
            }
            fragments.add(dayFrag);
            if (newDay.getTimeInMillis() == lastDay.getTimeInMillis()) {
                break;
            }
        }
    }

    /*
    * Inner class to act as the PagerAdapter for the viewpager.
    *
    * Implements various methods to assist with handling the pager.
     */
    class PagerAdapter extends FragmentPagerAdapter {
        private List<DayFragment> fragments;

        public PagerAdapter(android.support.v4.app.FragmentManager fm, List<DayFragment> fragments) {
            super(fm);
            this.fragments = fragments;
        }

        @Override
        public DayFragment getItem(int position) {
            return this.fragments.get(position);
        }

        @Override
        public int getCount() {
            return this.fragments.size();
        }

        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }
    }

    /*
     * Invoke this method (in onCreate()) for testing.
     * Allows the app to be started at a desired date instead of 'day'
     */
    private Calendar setDayForTesting(String test) {
        Date tester = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
            tester = (Date) sdf.parse(test);
        } catch (Exception e) {
            System.err.println(e);
        }

        Calendar cal = Calendar.getInstance();
        cal.setTime(tester);

        return cal;
    }

    /*
     * Generate options menu (from activity_dayview.xml in menu/)
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_dayview, menu);
        return true;
    }

    /******
     *
     * Activity methods
     */

    /*
     * When application resumes, refresh the UI of each day fragment..
     */
    public void onResume() {
        super.onResume();
        isPaused = false;
        if (pauseCounter > 0) {
            try {
                fragmentsRefreshUI();
            } catch (Exception e) {
                System.err.println(e);
            }
        }
        pauseCounter++;
    }

    @Override
    public void onPause() {
        super.onPause(); // Always call the superclass method first
        isPaused = true;
    }

    /******
     *
     * MENU METHODS
     */

    /*
     * Listen for click events on the options menu.
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
        case R.id.menu_about:
            Intent intent_about = new Intent(this, AboutActivity.class);
            startActivity(intent_about);
            return true;
        case R.id.menu_settings:
            Intent intent_settings = new Intent(this, PrferencesActivity.class);
            startActivity(intent_settings);
            return true;
        case R.id.menu_revert:
            toDay(null);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    /******
     *
     * BUTTON CLICK METHODS
     */
    /*
     * Executed if there's an issue loading the requested date. If exception thrown
     * assume date doesn't exist and make the error message visible.
     */
    private void dayNotFound() {
        ((TextView) findViewById(R.id.dayNotFound)).setVisibility(View.VISIBLE);
        ((ScrollView) findViewById(R.id.scrollView)).setVisibility(View.GONE);
    }

    /*
     * Set the current day to the current day. If error, assume no data for day.
     * (Hopefully this will never result in a error if app is updated!)
     */
    public void toDay(View view) {
        try {
            infoPager.setCurrentItem(todayFragmentIndex, true);
        } catch (Exception e) {
            System.err.println("Could not load day");
            dayNotFound();
        }
    }

    /*
    * Listen for clicks on MSW logo, and open up their site if clicked.
     */
    public void openMSW(View view) {
        String url = "http://www.magicseaweed.com";
        Intent i = new Intent(Intent.ACTION_VIEW);
        i.setData(Uri.parse(url));
        startActivity(i);
    }

    /*
    * Listen for clicks on 'location edit' image to open location select dialog.
     */
    public void editLocation(View view) {
        LocationDialog ld = new LocationDialog(locationNames, locationKeys);
        ld.show(getSupportFragmentManager(), "");

    }

    /*
    * Listen for clicks on 'surf detail' image to open the surf detail activity.
     */
    public void viewSurfDetail(View view) {
        Intent intent_surf = new Intent(this, SurfDetailActivity.class);
        Day d = ((DayFragment) fragments.get(currentFragmentIndex)).day;
        String loc = locationNames[locationIndex];
        intent_surf.putExtra("day", d);
        intent_surf.putExtra("location", loc);
        startActivity(intent_surf);
    }

    /*
    * If network available, sync surf and weather data
     */
    public void refresh(View view) {
        if (isSyncing) {
            return;
        }
        refreshButton.setVisibility(View.INVISIBLE);
        refreshProgress.setVisibility(View.VISIBLE);
        isSyncing = true;
        if (this.isOnline()) {
            new Utilities.DataGetter(this, locationKeys[locationIndex], weather_db).execute();
        } else {
            Toast.makeText(getApplicationContext(), "Unable to sync: network unavailable.", Toast.LENGTH_LONG)
                    .show();
            finishRefresh();
        }
    }

    /*
    * Called upon completion of the refresh task
     */
    public void notifySyncFinished(Boolean result) {
        finishRefresh();
        if (!result) {
            Toast.makeText(getApplicationContext(), "Sync error: Please try again later.", Toast.LENGTH_LONG)
                    .show();
        }
        isSyncing = false;
    }

    /******
     *
     * UTILITY METHODS
     */

    /*
     * Check for network connection. If Internet connection return true. Else, return false.
     */
    public boolean isOnline() {
        ConnectivityManager cm = (ConnectivityManager) getSystemService(
                getApplicationContext().CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo != null && netInfo.isConnectedOrConnecting()) {
            return true;
        }
        return false;
    }

    public void updateLocation(int index) {
        locationIndex = index;
        fragmentsRefreshUI();
        refresh(null);

        // Update prefs last in case there's an error with the location (as this would prevent app
        // from opening again!
        SharedPreferences.Editor editor = prefs.edit();
        editor.putInt("location_index", index);
        editor.commit();
    }

    /*
    * Update Fragments in ViewPager. Will only update the three Fragments currently in memory.
    *
    * These three methods execute different methods within the loaded pages.
     */
    private void fragmentsRefreshUI() {
        Utilities.reprocessDay(db, weather_db, ((DayFragment) fragments.get(currentFragmentIndex)).day,
                locationKeys[locationIndex]);
        ((DayFragment) fragments.get(currentFragmentIndex)).refreshUI();
        try {
            Utilities.reprocessDay(db, weather_db, ((DayFragment) fragments.get(currentFragmentIndex - 1)).day,
                    locationKeys[locationIndex]);
            ((DayFragment) fragments.get(currentFragmentIndex - 1)).refreshUI();
            Utilities.reprocessDay(db, weather_db, ((DayFragment) fragments.get(currentFragmentIndex + 1)).day,
                    locationKeys[locationIndex]);
            ((DayFragment) fragments.get(currentFragmentIndex + 1)).refreshUI();
        } catch (Exception e) {
            System.err.println("At end of viewpager.");
        }
    }

    /*
    * Called when network sync finishes, to update the UI (i.e. hide progress bar and re-show the button)
     */
    private void finishRefresh() {
        refreshProgress.setVisibility(View.INVISIBLE);
        refreshButton.setVisibility(View.VISIBLE);
        fragmentsRefreshUI();
    }

    /******
     *
     * INITIALIZATION METHODS
     */

    /*
     * Get the layout components initialized and make their variable names global.
     */
    private void initComponents() {
        infoPager = (ViewPager) findViewById(R.id.infoPager);
        dateText = (TextView) findViewById(R.id.dateText);
        buildProgress = (ProgressBar) findViewById(R.id.buildProgress);
        buildProgressHolder = (RelativeLayout) findViewById(R.id.buildProgressHolder);
        dateText.setTextColor(Color.rgb(0, 150, 220));
        revertButton = (ImageView) findViewById(R.id.revertButton);
        refreshButton = (ImageView) findViewById(R.id.refreshButton);
        refreshProgress = (ProgressBar) findViewById(R.id.refreshProgress);
    }

    /******
     *
     * THREADED CODE
     */

    /*
    * Threaded task to run startup routine (while progressbar shows on UI).
    *
    * Responsible for initialising view pager and other general tasks.
    * On finish, onPostExecute() is called.
     */
    private class StartupTasks extends AsyncTask<String, Integer, Boolean> {
        @Override
        protected Boolean doInBackground(String... arg0) {
            locationNames = getResources().getStringArray(R.array.locationDisplay);
            locationKeys = getResources().getIntArray(R.array.locationKey);
            locationIndex = prefs.getInt("location_index", 0);

            System.out.println("Initializing databases...");
            try {
                db = new DayDatabase(getApplicationContext());
                weather_db = new WeatherDatabase(getApplicationContext());
                firstDay = db.getFirstDay();
                lastDay = db.getLastDay();
            } catch (Exception e) {
                System.err.println(e);
            }
            System.out.println("Preparing day fragments...");
            currentDay = Calendar.getInstance();
            //currentDay = setDayForTesting("21/07/2016");

            System.out.println("Populating viewpager...");
            populatePager(currentDay, DAYS_TO_STORE);

            System.out.println("Setting listener...");
            infoPager.setOnPageChangeListener(new OnPageChangeListener() {
                public void onPageScrollStateChanged(int state) {
                }

                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                }

                public void onPageSelected(int position) {
                    currentFragmentIndex = position;
                    DayFragment myNow = mPagerAdapter.getItem(position);
                    myNow.slideSurf();
                    dateText.setText(myNow.day.toString());
                    if (position == todayFragmentIndex) {
                        revertButton.setVisibility(View.INVISIBLE);
                    } else {
                        revertButton.setVisibility(View.VISIBLE);
                    }
                }
            });

            /*
            * Set each Day's yesterday Day and tomorrow Day (if available)
             */
            for (int i = 0; i < fragments.size(); i++) {
                if (i > 0) {
                    Day yesterday = fragments.get(i - 1).day;
                    fragments.get(i).day.setYesterday(yesterday);
                }
                if (i < fragments.size() - 1) {
                    Day tomorrow = fragments.get(i + 1).day;
                    fragments.get(i).day.setTomorrow(tomorrow);
                }
            }
            return true;
        }

        protected void onPostExecute(Boolean result) {

            infoPager.setCurrentItem(todayFragmentIndex); // set initial pager position to current day

            /*
            * If preference is to sync weather on startup, then sync weather task now.
            * (this is done after initialising day fragments due to UI updates on the fragments during this task.)
             */
            if (prefs.getBoolean("sync_enabled", true)) {
                refresh(null);
            }

            System.out.println("Final bits...");
            ((RelativeLayout) findViewById(R.id.controls)).setVisibility(View.VISIBLE);
            buildProgressHolder.setVisibility(View.GONE);
            buildProgress.setVisibility(View.GONE);
            infoPager.setVisibility(View.VISIBLE);
            System.out.println("Done.");

        }
    }

}