can.yrt.onebusaway.RouteInfoListFragment.java Source code

Java tutorial

Introduction

Here is the source code for can.yrt.onebusaway.RouteInfoListFragment.java

Source

/*
 * Copyright (C) 2011 Paul Watts (paulcwatts@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package can.yrt.onebusaway;

import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import can.yrt.oba.ObaApi;
import can.yrt.oba.elements.ObaStop;
import can.yrt.oba.elements.ObaStopGroup;
import can.yrt.oba.elements.ObaStopGrouping;
import can.yrt.oba.provider.ObaContract;
import can.yrt.oba.request.ObaRouteRequest;
import can.yrt.oba.request.ObaRouteResponse;
import can.yrt.oba.request.ObaStopsForRouteRequest;
import can.yrt.oba.request.ObaStopsForRouteResponse;

import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListView;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RouteInfoListFragment extends ListFragment {

    private static final String TAG = "RouteInfoListFragment";

    private static final int ROUTE_INFO_LOADER = 0;

    private static final int ROUTE_STOPS_LOADER = 1;

    private String mRouteId;

    private ObaRouteResponse mRouteInfo;

    private StopsForRouteInfo mStopsForRoute;

    private SimpleExpandableListAdapter mAdapter;

    private final RouteLoaderCallback mRouteCallback = new RouteLoaderCallback();

    private final StopsLoaderCallback mStopsCallback = new StopsLoaderCallback();

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

        // We have a menu item to show in action bar.
        setHasOptionsMenu(true);
        registerForContextMenu(getListView());

        // Start out with a progress indicator.
        setListShown(false);

        // Initialize the expandable list
        ExpandableListView list = (ExpandableListView) getListView();
        list.setOnChildClickListener(mChildClick);

        // Get the route ID from the "uri" argument
        Uri uri = (Uri) getArguments().getParcelable(FragmentUtils.URI);
        if (uri == null) {
            Log.e(TAG, "No URI in arguments");
            return;
        }
        mRouteId = uri.getLastPathSegment();

        getLoaderManager().initLoader(ROUTE_INFO_LOADER, null, mRouteCallback);
        getLoaderManager().initLoader(ROUTE_STOPS_LOADER, null, mStopsCallback);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup root, Bundle savedInstanceState) {
        if (root == null) {
            // Currently in a layout without a container, so no
            // reason to create our view.
            return null;
        }
        return inflater.inflate(R.layout.route_info, null);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.route_info_options, menu);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        boolean hasUrl = false;
        if (mRouteInfo != null) {
            hasUrl = !TextUtils.isEmpty(mRouteInfo.getUrl());
        }
        menu.findItem(R.id.goto_url).setEnabled(hasUrl).setVisible(hasUrl);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        final int id = item.getItemId();
        if (id == R.id.show_on_map) {
            HomeActivity.start(getActivity(), mRouteId);
            return true;
        } else if (id == R.id.goto_url) {
            UIHelp.goToUrl(getActivity(), mRouteInfo.getUrl());
            return true;
        }
        return false;
    }

    private final ExpandableListView.OnChildClickListener mChildClick = new ExpandableListView.OnChildClickListener() {
        @Override
        public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition,
                long id) {
            showArrivals(v);
            return true;
        }
    };

    private static final int CONTEXT_MENU_DEFAULT = 1;

    private static final int CONTEXT_MENU_SHOWONMAP = 2;

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
        if (ExpandableListView
                .getPackedPositionType(info.packedPosition) != ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            return;
        }
        final TextView text = (TextView) info.targetView.findViewById(R.id.name);
        menu.setHeaderTitle(text.getText());
        menu.add(0, CONTEXT_MENU_DEFAULT, 0, R.string.route_info_context_get_stop_info);
        menu.add(0, CONTEXT_MENU_SHOWONMAP, 0, R.string.route_info_context_showonmap);
    }

    @Override
    public boolean onContextItemSelected(android.view.MenuItem item) {
        ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) item
                .getMenuInfo();
        switch (item.getItemId()) {
        case CONTEXT_MENU_DEFAULT:
            showArrivals(info.targetView);
            return true;
        case CONTEXT_MENU_SHOWONMAP:
            showOnMap(info.targetView);
            return true;
        default:
            return super.onContextItemSelected(item);
        }
    }

    private void showArrivals(View v) {
        final TextView text = (TextView) v.findViewById(R.id.stop_id);
        final String stopId = (String) text.getText();
        ObaStop stop = null;
        if (mStopsForRoute != null) {
            stop = mStopsForRoute.getStopMap().get(stopId);
        }
        ArrivalsListActivity.Builder b = new ArrivalsListActivity.Builder(getActivity(), stopId);
        if (stop != null) {
            b.setStopName(stop.getName());
            b.setStopDirection(stop.getDirection());
        }
        b.setUpMode(NavHelp.UP_MODE_BACK);
        b.start();
    }

    private void showOnMap(View v) {
        final TextView text = (TextView) v.findViewById(R.id.stop_id);
        final String stopId = (String) text.getText();
        // we need to find this route in the response because
        // we need to know it's lat/lon
        ObaStop stop = mStopsForRoute.getStopMap().get(stopId);
        if (stop == null) {
            return;
        }
        HomeActivity.start(getActivity(), stopId, stop.getLatitude(), stop.getLongitude());
    }

    //
    // Loader callbacks
    // This is simply because of our two loaders have different callback types,
    // so we can't easily implement them directly in the fragment.
    //
    private final class RouteLoaderCallback implements LoaderManager.LoaderCallbacks<ObaRouteResponse> {

        @Override
        public Loader<ObaRouteResponse> onCreateLoader(int id, Bundle args) {
            return new RouteInfoLoader(getActivity(), mRouteId);
        }

        @Override
        public void onLoadFinished(Loader<ObaRouteResponse> loader, ObaRouteResponse data) {
            setHeader(data, true);
        }

        @Override
        public void onLoaderReset(Loader<ObaRouteResponse> loader) {
            // Nothing to do right here...
        }
    }

    private final class StopsLoaderCallback implements LoaderManager.LoaderCallbacks<StopsForRouteInfo> {

        @Override
        public Loader<StopsForRouteInfo> onCreateLoader(int id, Bundle args) {
            return new StopsForRouteLoader(getActivity(), mRouteId);
        }

        @Override
        public void onLoadFinished(Loader<StopsForRouteInfo> loader, StopsForRouteInfo data) {
            setStopsForRoute(data);
        }

        @Override
        public void onLoaderReset(Loader<StopsForRouteInfo> loader) {
            // Nothing to do...
        }

    }

    //
    // Loader
    //
    private final static class RouteInfoLoader extends AsyncTaskLoader<ObaRouteResponse> {

        private final String mRouteId;

        RouteInfoLoader(Context context, String routeId) {
            super(context);
            mRouteId = routeId;
        }

        @Override
        public void onStartLoading() {
            forceLoad();
        }

        @Override
        public ObaRouteResponse loadInBackground() {
            return ObaRouteRequest.newRequest(getContext(), mRouteId).call();
        }
    }

    private final static class StopsForRouteLoader extends AsyncTaskLoader<StopsForRouteInfo> {

        private final String mRouteId;

        StopsForRouteLoader(Context context, String routeId) {
            super(context);
            mRouteId = routeId;
        }

        @Override
        public void onStartLoading() {
            forceLoad();
        }

        @Override
        public StopsForRouteInfo loadInBackground() {
            final ObaStopsForRouteResponse response = new ObaStopsForRouteRequest.Builder(getContext(), mRouteId)
                    .setIncludeShapes(false).build().call();
            return new StopsForRouteInfo(getContext(), response);
        }
    }

    private final static class StopsForRouteInfo {

        private final int mResultCode;

        private final ArrayList<HashMap<String, String>> mStopGroups;

        private final ArrayList<ArrayList<HashMap<String, String>>> mStops;

        private final HashMap<String, ObaStop> mStopMap;

        public StopsForRouteInfo(Context cxt, ObaStopsForRouteResponse response) {
            mStopGroups = new ArrayList<HashMap<String, String>>();
            mStops = new ArrayList<ArrayList<HashMap<String, String>>>();
            mStopMap = new HashMap<String, ObaStop>();
            mResultCode = response.getCode();
            initMaps(cxt, response);
        }

        private static Map<String, ObaStop> getStopMap(List<ObaStop> stops) {
            final int len = stops.size();
            HashMap<String, ObaStop> result = new HashMap<String, ObaStop>(len);
            for (int i = 0; i < len; ++i) {
                ObaStop stop = stops.get(i);
                result.put(stop.getId(), stop);
            }
            return result;
        }

        private void initMaps(Context cxt, ObaStopsForRouteResponse response) {
            // Convert to weird array type. From the documentation of
            // SimpleExpandableListAdapter:
            // StopGroupings: A List of Maps. Each entry in the List corresponds
            // to one group in the list. The Maps contain the data for each
            // group,
            // and should include all the entries specified in "groupFrom".
            //
            // StopGroupings: A List of Maps. Each entry in the List corresponds
            // to one group in the list. The Maps contain the data for each
            // group,
            // and should include all the entries specified in "groupFrom".
            //
            // Stops:
            // A List of List of Maps. Each entry in the outer List corresponds
            // to a group (index by group position), each entry in the inner
            // List
            // corresponds to a child within the group (index by child
            // position),
            // and the Map corresponds to the data for a child (index by values
            // in the childFrom array). The Map contains the data for each
            // child,
            // and should include all the entries specified in "childFrom"
            if (response.getCode() == ObaApi.OBA_OK) {
                final List<ObaStop> stops = response.getStops();
                final Map<String, ObaStop> stopMap = getStopMap(stops);
                final ObaStopGrouping[] groupings = response.getStopGroupings();
                final int groupingsLen = groupings.length;

                for (int groupingIndex = 0; groupingIndex < groupingsLen; ++groupingIndex) {
                    final ObaStopGrouping grouping = groupings[groupingIndex];
                    final ObaStopGroup[] groups = grouping.getStopGroups();
                    final int groupsLen = groups.length;

                    for (int i = 0; i < groupsLen; ++i) {
                        final HashMap<String, String> groupMap = new HashMap<String, String>(1);
                        final ObaStopGroup group = groups[i];
                        // We can initialize the stop grouping values.
                        groupMap.put("name", MyTextUtils.toTitleCase(group.getName()));
                        // Add this to the groupings map

                        // Create the sub list (the list of stops in the group)
                        final String[] stopIds = group.getStopIds();
                        final int stopIdLen = stopIds.length;

                        final ArrayList<HashMap<String, String>> childList = new ArrayList<HashMap<String, String>>(
                                stopIdLen);

                        for (int j = 0; j < stopIdLen; ++j) {
                            final String stopId = stopIds[j];
                            final ObaStop stop = stopMap.get(stopId);
                            HashMap<String, String> groupStopMap = new HashMap<String, String>(2);
                            if (stop != null) {
                                groupStopMap.put("name", MyTextUtils.toTitleCase(stop.getName()));
                                String dir = cxt.getString(UIHelp.getStopDirectionText(stop.getDirection()));
                                groupStopMap.put("direction", dir);
                                groupStopMap.put("id", stopId);
                                mStopMap.put(stopId, stop);
                            } else {
                                groupStopMap.put("name", "");
                                groupStopMap.put("direction", "");
                                groupStopMap.put("id", stopId);
                            }
                            childList.add(groupStopMap);
                        }

                        mStopGroups.add(groupMap);
                        mStops.add(childList);
                    }
                }
            }
        }

        public int getResultCode() {
            return mResultCode;
        }

        public ArrayList<HashMap<String, String>> getStopGroups() {
            return mStopGroups;
        }

        public ArrayList<ArrayList<HashMap<String, String>>> getStops() {
            return mStops;
        }

        public HashMap<String, ObaStop> getStopMap() {
            return mStopMap;
        }
    }

    //
    // Helper functions
    //
    private void setHeader(ObaRouteResponse routeInfo, boolean addToDb) {
        mRouteInfo = routeInfo;
        View view = getView();

        if (routeInfo.getCode() == ObaApi.OBA_OK) {
            TextView shortNameText = (TextView) view.findViewById(R.id.short_name);
            TextView longNameText = (TextView) view.findViewById(R.id.long_name);
            TextView agencyText = (TextView) view.findViewById(R.id.agency);
            String url = mRouteInfo.getUrl();

            String shortName = routeInfo.getShortName();
            String longName = routeInfo.getLongName();

            if (TextUtils.isEmpty(shortName)) {
                shortName = longName;
            }
            if (TextUtils.isEmpty(longName) || shortName.equals(longName)) {
                longName = routeInfo.getDescription();
            }

            shortNameText.setText(shortName);
            longNameText.setText(longName);
            agencyText.setText(mRouteInfo.getAgency().getName());

            if (addToDb) {
                ContentValues values = new ContentValues();
                values.put(ObaContract.Routes.SHORTNAME, shortName);
                values.put(ObaContract.Routes.LONGNAME, longName);
                values.put(ObaContract.Routes.URL, url);
                if (Application.get().getCurrentRegion() != null) {
                    values.put(ObaContract.Routes.REGION_ID, Application.get().getCurrentRegion().getId());
                }
                ObaContract.Routes.insertOrUpdate(getActivity(), mRouteInfo.getId(), values, true);
            }
        } else {
            setEmptyText(getString(UIHelp.getRouteErrorString(getActivity(), routeInfo.getCode())));
        }
    }

    private void setStopsForRoute(StopsForRouteInfo result) {
        mStopsForRoute = result;
        final int code = mStopsForRoute.getResultCode();
        if (code == ObaApi.OBA_OK) {
            setEmptyText("");
        } else {
            setEmptyText(getString(UIHelp.getRouteErrorString(getActivity(), code)));
        }
        mAdapter = new SimpleExpandableListAdapter(getActivity(), result.getStopGroups(),
                android.R.layout.simple_expandable_list_item_1, new String[] { "name" },
                new int[] { android.R.id.text1 }, result.getStops(), R.layout.route_info_listitem,
                new String[] { "name", "direction", "id" }, new int[] { R.id.name, R.id.direction, R.id.stop_id });
        setListAdapter(mAdapter);
    }

    public void setListAdapter(SimpleExpandableListAdapter adapter) {
        ExpandableListView list = (ExpandableListView) getListView();

        if (list != null) {
            list.setAdapter(adapter);
            setListShown(true);
        }
    }
}