nz.co.wholemeal.christchurchmetro.FavouritesActivity.java Source code

Java tutorial

Introduction

Here is the source code for nz.co.wholemeal.christchurchmetro.FavouritesActivity.java

Source

/**
 * Copyright 2010 Malcolm Locke
 *
 * 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 nz.co.wholemeal.christchurchmetro;

import java.util.ArrayList;
import java.util.Iterator;

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

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.TextView;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

/*
 * Some of the authors favourites:
 * "40188", "20763", "21450", "37375", "37334", "14864", "21957"
 */

public class FavouritesActivity extends ListActivity implements LoadRoutesActivity {

    public final static String TAG = "FavouritesActivity";
    // Values for maximum in the progress dialog.  Hopefully will not fluctuate
    // much over time.
    private static int MAX_PLATFORMS = 2600;
    private static int MAX_ROUTES = 125;

    public static ArrayList stops = new ArrayList<Stop>();
    private StopAdapter stopAdapter;
    private AsyncLoadPlatforms asyncLoadPlatforms = null;
    private ProgressDialog loadingRoutesProgressDialog = null;

    static final int DIALOG_LOAD_DATA = 0;

    private boolean loadDataDialogShown = false;

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

        setContentView(R.layout.favourites_list);

        if (stops.size() == 0) {
            initFavourites();
        }

        stopAdapter = new StopAdapter(this, R.layout.stop_list_item, stops);
        setListAdapter(stopAdapter);

        ListView lv = getListView();

        /* Enables the long click in the ListView to be handled in this Activity */
        registerForContextMenu(lv);

        lv.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = new Intent();
                Stop stop = (Stop) stops.get(position);

                if (stop == null) {
                    Log.e(TAG, "Didn't get a stop");
                    finish();
                }
                intent.putExtra("platformTag", stop.platformTag);
                intent.setClassName("nz.co.wholemeal.christchurchmetro",
                        "nz.co.wholemeal.christchurchmetro.PlatformActivity");

                startActivity(intent);
            }
        });

        /*
         * This will contain return non null if we received an orientation change
         */
        asyncLoadPlatforms = (AsyncLoadPlatforms) getLastNonConfigurationInstance();
        if (asyncLoadPlatforms != null) {
            loadDataDialogShown = true;
            initProgressDialog();
            asyncLoadPlatforms.attach(this);
        }

        SharedPreferences preferences = getSharedPreferences(PlatformActivity.PREFERENCES_FILE, 0);
        if (preferences.getLong("lastDataLoad", -1) == -1 && !loadDataDialogShown) {
            showDialog(DIALOG_LOAD_DATA);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        /*
         * Other activities can modify the favourites list, so reload every time
         * we come back to the foreground
         */
        stopAdapter.notifyDataSetChanged();
    }

    /*
     * This is used to handle rotation while the 'loading routes' dialog
     * is being displayed.
     */
    @Override
    public Object onRetainNonConfigurationInstance() {
        if (asyncLoadPlatforms != null) {
            asyncLoadPlatforms.detach();
        }
        if (loadingRoutesProgressDialog != null) {
            loadingRoutesProgressDialog.dismiss();
        }
        return asyncLoadPlatforms;
    }

    public void showLoadingRoutesProgressDialog() {
        initProgressDialog();
    }

    private void initProgressDialog() {
        loadingRoutesProgressDialog = new ProgressDialog(this);
        loadingRoutesProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        loadingRoutesProgressDialog.setCancelable(false);

        loadingRoutesProgressDialog.setMax(MAX_PLATFORMS);
        loadingRoutesProgressDialog.setMessage(getString(R.string.loading_platforms));
        loadingRoutesProgressDialog.show();
    }

    public void updateLoadingRoutesProgressDialog(int progress) {
        // This is a special value, and means the import mode has
        // progressed from platforms to patterns
        if (progress == -1) {
            loadingRoutesProgressDialog.setMax(MAX_ROUTES);
            loadingRoutesProgressDialog.setMessage(getString(R.string.loading_routes));
        } else {
            loadingRoutesProgressDialog.setProgress(progress);
        }
    }

    public void loadingRoutesComplete(String message) {
        loadingRoutesProgressDialog.dismiss();
        asyncLoadPlatforms = null;
        Toast.makeText(getBaseContext(), message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.setHeaderTitle(R.string.options);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.favourite_context_menu, menu);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        Stop stop = (Stop) stops.get((int) info.id);
        switch (item.getItemId()) {
        case R.id.remove_favourite:
            removeFavourite(stop);
            return true;
        default:
            return super.onContextItemSelected(item);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent intent;
        switch (item.getItemId()) {
        case R.id.map:
            Log.d(TAG, "Map selected from menu");
            intent = new Intent();
            intent.setClassName("nz.co.wholemeal.christchurchmetro",
                    "nz.co.wholemeal.christchurchmetro.MetroMapActivity");
            startActivity(intent);
            return true;
        case R.id.search:
            Log.d(TAG, "Search selected from menu");
            onSearchRequested();
            return true;
        case R.id.routes:
            Log.d(TAG, "Routes selected from menu");
            intent = new Intent();
            intent.setClassName("nz.co.wholemeal.christchurchmetro",
                    "nz.co.wholemeal.christchurchmetro.RoutesActivity");
            startActivity(intent);
            return true;
        case R.id.preferences:
            Log.d(TAG, "Preferences selected from menu");
            intent = new Intent();
            intent.setClassName("nz.co.wholemeal.christchurchmetro",
                    "nz.co.wholemeal.christchurchmetro.PreferencesActivity");
            startActivity(intent);
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private void initFavourites() {
        SharedPreferences favourites = getSharedPreferences(PlatformActivity.PREFERENCES_FILE, 0);
        String stops_json = favourites.getString("favouriteStops", null);

        if (stops_json != null) {
            Log.d(TAG, "initFavourites(): stops_json = " + stops_json);
            try {
                ArrayList favouriteStops = new ArrayList<Stop>();
                JSONArray stopsArray = (JSONArray) new JSONTokener(stops_json).nextValue();

                for (int i = 0; i < stopsArray.length(); i++) {
                    try {
                        String platformTag = (String) stopsArray.get(i);
                        Log.d(TAG, "Loading stop platformTag = " + platformTag);
                        Stop stop = new Stop(platformTag, null, getApplicationContext());
                        favouriteStops.add(stop);
                        Log.d(TAG, "initFavourites(): added stop platformTag = " + stop.platformTag);
                    } catch (Stop.InvalidPlatformNumberException e) {
                        Log.e(TAG, "Invalid platformTag in favourites: " + e.getMessage());
                    } catch (JSONException e) {
                        Log.e(TAG, "JSONException() parsing favourites: " + e.getMessage());
                    }
                }

                if (favouriteStops.size() > 0) {
                    stops.addAll(favouriteStops);
                }
            } catch (JSONException e) {
                Log.e(TAG, "initFavourites(): JSONException: " + e.toString());
            }
        }
    }

    public void saveFavourites() {
        SharedPreferences favourites = getSharedPreferences(PlatformActivity.PREFERENCES_FILE, 0);
        saveFavourites(favourites);
        stopAdapter.notifyDataSetChanged();
    }

    public static void saveFavourites(SharedPreferences favourites) {
        SharedPreferences.Editor editor = favourites.edit();
        JSONArray stopArray = new JSONArray();
        Iterator iterator = stops.iterator();
        while (iterator.hasNext()) {
            Stop stop = (Stop) iterator.next();
            stopArray.put(stop.platformTag);
        }
        editor.putString("favouriteStops", stopArray.toString());
        Log.d(TAG, "Saving " + stops.size() + " favourites");
        Log.d(TAG, "json = " + stopArray.toString());
        editor.commit();
    }

    public static boolean isFavourite(Stop stop) {
        Iterator iterator = stops.iterator();

        /* Check the Stop is not already present in favourites */
        while (iterator.hasNext()) {
            Stop favourite = (Stop) iterator.next();
            if (favourite.platformTag.equals(stop.platformTag)) {
                return true;
            }
        }

        return false;
    }

    public void removeFavourite(Stop stop) {
        if (isFavourite(stop)) {
            Log.d(TAG, "Removed stop " + stop.platformNumber + " from favourites");
            stops.remove(stop);
            saveFavourites();
        } else {
            Log.e(TAG, "Remove requested for stop " + stop.platformNumber + " but it's not present in favourites");
        }
    }

    public void removeFavourite(SharedPreferences preferences, Stop stop) {
        if (isFavourite(stop)) {
            Log.d(TAG, "Removed stop " + stop.platformNumber + " from favourites");
            stops.remove(stop);
            saveFavourites();
        } else {
            Log.e(TAG, "Remove requested for stop " + stop.platformNumber + " but it's not present in favourites");
        }
    }

    protected Dialog onCreateDialog(int id) {
        Dialog dialog;
        switch (id) {
        case DIALOG_LOAD_DATA:
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setCancelable(true).setTitle(R.string.route_update_required)
                    .setMessage(R.string.do_you_want_to_load_bus_stop_and_route_data)
                    .setPositiveButton(R.string.load_now, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                            asyncLoadPlatforms = new AsyncLoadPlatforms(
                                    (LoadRoutesActivity) FavouritesActivity.this);
                            asyncLoadPlatforms.execute();
                        }
                    }).setNegativeButton(R.string.do_it_later, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });
            dialog = builder.create();
            break;
        default:
            dialog = null;
        }
        return dialog;
    }

    private class StopAdapter extends ArrayAdapter<Stop> {

        private ArrayList<Stop> items;

        public StopAdapter(Context context, int textViewResourceId, ArrayList<Stop> items) {
            super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.stop_list_item, null);
            }
            Stop stop = items.get(position);
            if (stop != null) {
                TextView platformNumber = (TextView) v.findViewById(R.id.platform_number);
                TextView platformName = (TextView) v.findViewById(R.id.platform_name);
                TextView nextBus = (TextView) v.findViewById(R.id.next_bus);
                platformNumber.setText(stop.platformNumber);
                platformName.setText(stop.name);
                nextBus.setTag(stop);
                nextBus.setText(R.string.next_bus_loading);
                new AsyncNextArrival().execute(nextBus);
            }
            return v;
        }
    }

    /* Load next arrival for each favourite in the background */
    public class AsyncNextArrival extends AsyncTask<TextView, Void, TextView> {

        private String arrivalText = null;

        protected TextView doInBackground(TextView... textViews) {
            TextView textView = textViews[0];
            Stop stop = (Stop) textView.getTag();
            Arrival arrival = null;
            ArrayList arrivals = null;
            Log.d(TAG, "Running AsyncNextArrival.doInBackground() for stop " + stop.platformNumber);
            try {
                arrivals = stop.getArrivals();
            } catch (Exception e) {
                arrivalText = getString(R.string.unable_to_retrieve_information);
            }

            if (arrivals != null) {
                if (!arrivals.isEmpty()) {
                    arrival = (Arrival) arrivals.get(0);
                    arrivalText = getResources().getQuantityString(R.plurals.mins, arrival.eta, arrival.eta) + ": "
                            + arrival.routeNumber + " - " + arrival.destination;
                } else {
                    arrivalText = getString(R.string.no_buses_due);
                }
            }
            return textView;
        }

        protected void onPostExecute(TextView textView) {
            textView.setText(arrivalText);
        }
    }

}