org.onebusaway.android.ui.TripPlanFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.onebusaway.android.ui.TripPlanFragment.java

Source

/*
 * Copyright (C) 2012-2013 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 org.onebusaway.android.ui;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;

import org.onebusaway.android.R;
import org.onebusaway.android.app.Application;
import org.onebusaway.android.directions.util.ConversionUtils;
import org.onebusaway.android.directions.util.CustomAddress;
import org.onebusaway.android.directions.util.OTPConstants;
import org.onebusaway.android.directions.util.PlacesAutoCompleteAdapter;
import org.onebusaway.android.directions.util.TripRequestBuilder;
import org.onebusaway.android.io.ObaAnalytics;
import org.onebusaway.android.io.elements.ObaRegion;
import org.onebusaway.android.map.googlemapsv2.ProprietaryMapHelpV2;
import org.onebusaway.android.util.LocationUtils;
import org.onebusaway.android.util.PreferenceUtils;
import org.onebusaway.android.util.UIUtils;

import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.TypedArray;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.CheckBox;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

public class TripPlanFragment extends Fragment {

    public TripPlanFragment setPlanErrorUrl(String planErrorUrl) {
        mPlanErrorUrl = planErrorUrl;
        return this;
    }

    public TripPlanFragment setPlanRequestUrl(String planRequestUrl) {
        mPlanRequestUrl = planRequestUrl;
        return this;
    }

    /**
     * Allows calling activity to register to know when to send request.
     */
    interface Listener {

        /**
         * Called when the fields have been populated and a trip plan can occur.
         */
        void onTripRequestReady();
    }

    private static final int USE_FROM_ADDRESS = 1;
    private static final int USE_TO_ADDRESS = 2;

    private AutoCompleteTextView mFromAddressTextArea;
    private AutoCompleteTextView mToAddressTextArea;
    private ImageButton mFromCurrentLocationImageButton;
    private ImageButton mToCurrentLocationImageButton;
    private Spinner mDate;
    private ArrayAdapter mDateAdapter;
    private Spinner mTime;
    private ArrayAdapter mTimeAdapter;
    private Spinner mLeavingChoice;
    ArrayAdapter<CharSequence> mLeavingChoiceAdapter;

    Calendar mMyCalendar;

    protected GoogleApiClient mGoogleApiClient;
    private static final String TAG = "TripPlanFragment";

    private CustomAddress mFromAddress, mToAddress;

    private TripRequestBuilder mBuilder;

    private Listener mListener;

    private String mPlanErrorUrl;

    private String mPlanRequestUrl;

    private void resetDateTimeLabels() {
        String dateText = new SimpleDateFormat(OTPConstants.TRIP_PLAN_DATE_STRING_FORMAT, Locale.getDefault())
                .format(mMyCalendar.getTime());
        String timeText = new SimpleDateFormat(OTPConstants.TRIP_PLAN_TIME_STRING_FORMAT, Locale.getDefault())
                .format(mMyCalendar.getTime());

        mDateAdapter.insert(dateText, 0);
        mDateAdapter.notifyDataSetChanged();

        mTimeAdapter.insert(timeText, 0);
        mTimeAdapter.notifyDataSetChanged();
    }

    // Create view, initialize state
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Init Google Play Services as early as possible in the Fragment lifecycle to give it time
        if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(getActivity()) == ConnectionResult.SUCCESS) {
            mGoogleApiClient = LocationUtils.getGoogleApiClientWithCallbacks(getContext());
            mGoogleApiClient.connect();
        }

        Bundle bundle = getArguments();
        mBuilder = new TripRequestBuilder(bundle);

        final View view = inflater.inflate(R.layout.fragment_trip_plan, container, false);
        setHasOptionsMenu(true);

        mFromAddressTextArea = (AutoCompleteTextView) view.findViewById(R.id.fromAddressTextArea);
        mToAddressTextArea = (AutoCompleteTextView) view.findViewById(R.id.toAddressTextArea);
        mFromCurrentLocationImageButton = (ImageButton) view.findViewById(R.id.fromCurrentLocationImageButton);
        mToCurrentLocationImageButton = (ImageButton) view.findViewById(R.id.toCurrentLocationImageButton);
        mDate = (Spinner) view.findViewById(R.id.date);
        mDateAdapter = new ArrayAdapter(getActivity(), R.layout.simple_list_item);
        mDate.setAdapter(mDateAdapter);

        mTime = (Spinner) view.findViewById(R.id.time);
        mTimeAdapter = new ArrayAdapter(getActivity(), R.layout.simple_list_item);
        mTime.setAdapter(mTimeAdapter);

        mLeavingChoice = (Spinner) view.findViewById(R.id.leavingChoiceSpinner);

        mLeavingChoiceAdapter = ArrayAdapter.createFromResource(getContext(),
                R.array.trip_plan_leaving_arriving_array, R.layout.simple_list_item);
        mLeavingChoiceAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mLeavingChoice.setAdapter(mLeavingChoiceAdapter);

        // Set mLeavingChoice onclick adapter in onResume() so we do not fire it when setting it
        final TimePickerDialog.OnTimeSetListener timeCallback = new TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker view, int hour, int minute) {
                mMyCalendar.set(Calendar.HOUR_OF_DAY, hour);
                mMyCalendar.set(Calendar.MINUTE, minute);
                resetDateTimeLabels();
                mBuilder.setDateTime(mMyCalendar);
                checkRequestAndSubmit();
            }
        };

        final DatePickerDialog.OnDateSetListener dateCallback = new DatePickerDialog.OnDateSetListener() {

            @Override
            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                mMyCalendar.set(Calendar.YEAR, year);
                mMyCalendar.set(Calendar.MONTH, monthOfYear);
                mMyCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
                resetDateTimeLabels();
                mBuilder.setDateTime(mMyCalendar);
                checkRequestAndSubmit();
            }

        };

        mDate.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                    new DatePickerDialog(view.getContext(), dateCallback, mMyCalendar.get(Calendar.YEAR),
                            mMyCalendar.get(Calendar.MONTH), mMyCalendar.get(Calendar.DAY_OF_MONTH)).show();
                }

                return true;
            }
        });

        mTime.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                    new TimePickerDialog(view.getContext(), timeCallback, mMyCalendar.get(Calendar.HOUR_OF_DAY),
                            mMyCalendar.get(Calendar.MINUTE), false).show();
                }
                return true;
            }
        });

        ImageButton resetTimeButton = (ImageButton) view.findViewById(R.id.resetTimeImageButton);

        resetTimeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mMyCalendar = Calendar.getInstance();
                mBuilder.setDateTime(mMyCalendar);
                resetDateTimeLabels();
                checkRequestAndSubmit();
            }
        });

        setUpAutocomplete(mFromAddressTextArea, USE_FROM_ADDRESS);
        setUpAutocomplete(mToAddressTextArea, USE_TO_ADDRESS);

        mToCurrentLocationImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mToAddressTextArea.setText(getString(R.string.tripplanner_current_location));
                mToAddress = makeAddressFromLocation();
                mBuilder.setTo(mToAddress);
                checkRequestAndSubmit();
            }
        });

        mFromCurrentLocationImageButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mFromAddressTextArea.setText(getString(R.string.tripplanner_current_location));
                mFromAddress = makeAddressFromLocation();
                mBuilder.setFrom(mFromAddress);
                checkRequestAndSubmit();
            }
        });

        // Start: default from address is Current Location, to address is unset
        return view;
    }

    private void checkRequestAndSubmit() {
        if (mBuilder.ready() && mListener != null) {
            mListener.onTripRequestReady();
        }
    }

    // Populate data fields
    @Override
    public void onResume() {
        super.onResume();

        mFromAddress = mBuilder.getFrom();

        if (mFromAddress == null) {
            mFromAddress = makeAddressFromLocation();
            mBuilder.setFrom(mFromAddress);
        }

        setAddressText(mFromAddressTextArea, mFromAddress);

        mToAddress = mBuilder.getTo();

        if (mToAddress == null) {
            mToAddress = CustomAddress.getEmptyAddress();
            mBuilder.setTo(mToAddress);
        }

        setAddressText(mToAddressTextArea, mToAddress);

        boolean arriving = mBuilder.getArriveBy();

        if (mMyCalendar == null) {
            Date date = mBuilder.getDateTime();
            if (date == null) {
                date = new Date();
            }
            mMyCalendar = Calendar.getInstance();
            mMyCalendar.setTime(date);

            if (arriving) {
                mBuilder.setArrivalTime(mMyCalendar);
            } else {
                mBuilder.setDepartureTime(mMyCalendar);
            }
        }

        resetDateTimeLabels();

        String leavingChoice = getString(arriving ? R.string.trip_plan_arriving : R.string.trip_plan_leaving);
        mLeavingChoice.setSelection(mLeavingChoiceAdapter.getPosition(leavingChoice), false);

        mLeavingChoice.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

                String item = (String) parent.getItemAtPosition(position);

                if (item.equals(getString(R.string.trip_plan_arriving))) {
                    mBuilder.setArrivalTime(mMyCalendar);
                } else {
                    mBuilder.setDepartureTime(mMyCalendar);
                }
                checkRequestAndSubmit();
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                // do nothing
            }
        });
    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        switch (id) {
        case R.id.action_settings:
            advancedSettings();
            return true;
        case R.id.action_reverse:
            reverseTrip();
            return true;
        case R.id.action_report_trip_problem:
            reportTripPlanProblem();
            return true;
        default:
            break;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * Set Listener of this fragment.
     */
    public void setListener(Listener listener) {
        mListener = listener;
    }

    private void advancedSettings() {

        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());

        final TypedArray transitModeResource = getContext().getResources()
                .obtainTypedArray(R.array.transit_mode_array);
        final boolean unitsAreImperial = !PreferenceUtils.getUnitsAreMetricFromPreferences(getContext());

        dialogBuilder.setTitle(R.string.trip_plan_advanced_settings)
                .setView(R.layout.trip_plan_advanced_settings_dialog);

        dialogBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int which) {

                Dialog dialog = (Dialog) dialogInterface;

                boolean optimizeTransfers = ((CheckBox) dialog.findViewById(R.id.checkbox_minimize_transfers))
                        .isChecked();

                Spinner spinnerTravelBy = (Spinner) dialog.findViewById(R.id.spinner_travel_by);

                int modeId = transitModeResource.getResourceId(spinnerTravelBy.getSelectedItemPosition(), 0);

                boolean wheelchair = ((CheckBox) dialog.findViewById(R.id.checkbox_wheelchair_acccesible))
                        .isChecked();

                String maxWalkString = ((EditText) dialog.findViewById(R.id.number_maximum_walk_distance)).getText()
                        .toString();
                double maxWalkDistance;
                if (TextUtils.isEmpty(maxWalkString)) {
                    maxWalkDistance = Double.MAX_VALUE;
                } else {
                    double d = Double.parseDouble(maxWalkString);
                    maxWalkDistance = unitsAreImperial ? ConversionUtils.feetToMeters(d) : d;
                }

                mBuilder.setOptimizeTransfers(optimizeTransfers).setModeSetById(modeId)
                        .setWheelchairAccessible(wheelchair).setMaxWalkDistance(maxWalkDistance);

                checkRequestAndSubmit();
            }
        });

        final AlertDialog dialog = dialogBuilder.create();

        dialog.show();

        CheckBox minimizeTransfersCheckbox = (CheckBox) dialog.findViewById(R.id.checkbox_minimize_transfers);
        Spinner spinnerTravelBy = (Spinner) dialog.findViewById(R.id.spinner_travel_by);
        CheckBox wheelchairCheckbox = (CheckBox) dialog.findViewById(R.id.checkbox_wheelchair_acccesible);
        EditText maxWalkEditText = (EditText) dialog.findViewById(R.id.number_maximum_walk_distance);

        minimizeTransfersCheckbox.setChecked(mBuilder.getOptimizeTransfers());

        wheelchairCheckbox.setChecked(mBuilder.getWheelchairAccessible());

        ArrayAdapter adapter = ArrayAdapter.createFromResource(getActivity(), R.array.transit_mode_array,
                android.R.layout.simple_spinner_item);

        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerTravelBy.setAdapter(adapter);

        int modeSetId = mBuilder.getModeSetId();
        if (modeSetId != -1) {
            for (int i = 0; i < transitModeResource.length(); i++) {
                if (transitModeResource.getResourceId(i, -1) == modeSetId) {
                    spinnerTravelBy.setSelection(i);
                    break;
                }
            }
        }

        Double maxWalk = mBuilder.getMaxWalkDistance();
        if (maxWalk != null && Double.MAX_VALUE != maxWalk) {
            if (unitsAreImperial) {
                maxWalk = ConversionUtils.metersToFeet(maxWalk);
            }
            maxWalkEditText.setText(String.format("%d", maxWalk.longValue()));
        }

        if (unitsAreImperial) {
            TextView label = (TextView) dialog.findViewById(R.id.label_minimum_walk_distance);
            label.setText(getString(R.string.feet_abbreviation));
        }
    }

    private void reverseTrip() {
        mFromAddress = mBuilder.getTo();
        mToAddress = mBuilder.getFrom();

        mBuilder.setFrom(mFromAddress).setTo(mToAddress);

        setAddressText(mFromAddressTextArea, mFromAddress);
        setAddressText(mToAddressTextArea, mToAddress);

        if (mBuilder.ready() && mListener != null) {
            mListener.onTripRequestReady();
        }
    }

    private void reportTripPlanProblem() {
        String email = Application.get().getCurrentRegion().getOtpContactEmail();
        if (!TextUtils.isEmpty(email)) {
            Location loc = Application.getLastKnownLocation(getActivity().getApplicationContext(), null);
            String locString = null;
            if (loc != null) {
                locString = LocationUtils.printLocationDetails(loc);
            }
            if (mPlanErrorUrl != null) {
                UIUtils.sendEmail(getActivity(), email, locString, mPlanErrorUrl, true);
            } else if (mPlanRequestUrl != null) {
                UIUtils.sendEmail(getActivity(), email, locString, mPlanRequestUrl, false);
            } else {
                UIUtils.sendEmail(getActivity(), email, locString, null, true);
            }
            ObaAnalytics.reportEventWithCategory(ObaAnalytics.ObaEventCategory.UI_ACTION.toString(),
                    getString(R.string.analytics_action_problem),
                    getString(R.string.analytics_label_app_feedback_otp));
        }
    }

    private void setAddressText(TextView tv, CustomAddress address) {
        if (address != null && address.getAddressLine(0) != null) {
            tv.setText(address.toString());
        } else {
            tv.setText(null);
        }
    }

    private CustomAddress makeAddressFromLocation() {
        CustomAddress address = CustomAddress.getEmptyAddress();

        Location loc = Application.getLastKnownLocation(getContext(), mGoogleApiClient);
        if (loc == null) {
            if (getContext() != null) {
                Toast.makeText(getContext(), getString(R.string.main_location_unavailable), Toast.LENGTH_SHORT)
                        .show();
            }
        } else {
            address.setLatitude(loc.getLatitude());
            address.setLongitude(loc.getLongitude());
        }

        address.setAddressLine(0, getString(R.string.tripplanner_current_location));
        return address;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (resultCode != -1) {
            Log.e(TAG, "Error getting geocoding results");
            return;
        }

        CustomAddress address = ProprietaryMapHelpV2
                .getCustomAddressFromPlacesIntent(Application.get().getApplicationContext(), intent);

        // Note that onResume will run after this function. We need to put new objects in the bundle.
        if (requestCode == USE_FROM_ADDRESS) {
            mFromAddress = address;
            mBuilder.setFrom(mFromAddress);
            mFromAddressTextArea.setText(mFromAddress.toString());
        } else if (requestCode == USE_TO_ADDRESS) {
            mToAddress = address;
            mBuilder.setTo(mToAddress);
            mToAddressTextArea.setText(mToAddress.toString());
        }

        checkRequestAndSubmit();
    }

    private void setUpAutocomplete(AutoCompleteTextView tv, final int use) {
        ObaRegion region = Application.get().getCurrentRegion();

        // Use Google widget if available
        if (GoogleApiAvailability.getInstance()
                .isGooglePlayServicesAvailable(getContext()) == ConnectionResult.SUCCESS) {
            tv.setFocusable(false);
            tv.setOnClickListener(new ProprietaryMapHelpV2.StartPlacesAutocompleteOnClick(use, this, region));
            return;
        }

        // else, set up autocomplete with Android geocoder

        tv.setAdapter(new PlacesAutoCompleteAdapter(getContext(), android.R.layout.simple_list_item_1, region));
        tv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                CustomAddress addr = (CustomAddress) parent.getAdapter().getItem(position);

                if (use == USE_FROM_ADDRESS) {
                    mFromAddress = addr;
                    mBuilder.setFrom(mFromAddress);
                } else if (use == USE_TO_ADDRESS) {
                    mToAddress = addr;
                    mBuilder.setTo(mToAddress);
                }

                checkRequestAndSubmit();
            }
        });
    }
}