/** * Copyright (C) 2013 Romain Guefveneu. * * This file is part of naonedbus. * * Naonedbus 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. * * Naonedbus 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 net.naonedbus.fragment.impl; import java.text.DateFormat; import java.util.ArrayList; import java.util.List; import net.naonedbus.NBApplication; import net.naonedbus.R; import net.naonedbus.activity.impl.AddressSearchActivity; import net.naonedbus.bean.Equipement; import net.naonedbus.bean.Equipement.Type; import net.naonedbus.bean.ItineraryWrapper; import net.naonedbus.bean.async.AsyncResult; import net.naonedbus.fragment.AbstractListFragment; import net.naonedbus.loader.ItineraryLoader; import net.naonedbus.provider.impl.NaoLocationManager; import net.naonedbus.provider.impl.NaoLocationManager.NaoLocationListener; import net.naonedbus.task.AddressResolverTask; import net.naonedbus.task.AddressResolverTask.AddressTaskListener; import net.naonedbus.utils.ColorUtils; import net.naonedbus.utils.FormatUtils; import net.naonedbus.widget.adapter.impl.DateKindSpinnerAdaper; import net.naonedbus.widget.adapter.impl.ItineraryWrapperArrayAdapter; import org.joda.time.MutableDateTime; import android.app.DatePickerDialog; import android.app.DatePickerDialog.OnDateSetListener; import android.app.TimePickerDialog; import android.app.TimePickerDialog.OnTimeSetListener; import android.content.Intent; import android.graphics.Color; import android.location.Address; import android.location.Location; import android.location.LocationManager; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.DialogFragment; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.DatePicker; import android.widget.ImageView; import android.widget.ListView; import android.widget.Spinner; import android.widget.TextView; import android.widget.TimePicker; import com.bugsense.trace.BugSenseHandler; public class ItineraireFragment extends AbstractListFragment implements LoaderCallbacks<AsyncResult<List<ItineraryWrapper>>>, OnDateSetListener, OnTimeSetListener, NaoLocationListener { private static final String TAG = "ItineraireFragment"; private static final String BUNDLE_LOCATION_FROM = "ItineraireFragment:locationFrom"; private static final String BUNDLE_LOCATION_TO = "ItineraireFragment:locationTo"; private static final String BUNDLE_ADDRESS_FROM = "ItineraireFragment:addressFrom"; private static final String BUNDLE_ADDRESS_TO = "ItineraireFragment:addressTo"; private static final String BUNDLE_DATE_TIME = "ItineraireFragment:dateTime"; private static final String BUNDLE_ARRIVE_BY = "ItineraireFragment:arriveBy"; private static final String BUNDLE_ICON_FROM = "ItineraireFragment:iconFrom"; private static final String BUNDLE_ICON_TO = "ItineraireFragment:iconTo"; private static final String BUNDLE_COLOR_FROM = "ItineraireFragment:colorFrom"; private static final String BUNDLE_COLOR_TO = "ItineraireFragment:colorTo"; private static final String BUNDLE_RESULTS = "ItineraireFragment:results"; private static final int REQUEST_CODE_FROM = 1; private static final int REQUEST_CODE_TO = 2; private final List<ItineraryWrapper> mItineraryWrappers = new ArrayList<ItineraryWrapper>(); private ItineraryWrapperArrayAdapter mAdapter; private View mProgressView; private DateFormat mDateFormat; private AddressResolverTask mAddressResolverTask; private final NaoLocationManager mLocationProvider; private final Location mFromLocation = new Location(LocationManager.GPS_PROVIDER); private final Location mToLocation = new Location(LocationManager.GPS_PROVIDER); private final MutableDateTime mDateTime = new MutableDateTime(); private boolean mArriveBy; private boolean mFromCurrentLocation; private boolean mToCurrentLocation; private TextView mFromAddressTextView; private TextView mToAddressTextView; private TextView mDateAndTimeLabel; private Spinner mDateKindSpinner; private View mReverseButton; private ImageView mIconFrom; private ImageView mIconTo; private int mIconFromColor = Color.TRANSPARENT; private int mIconToColor = Color.TRANSPARENT; private int mIconFromResId = R.drawable.ic_directions_form_destination_notselected; private int mIconToResId = R.drawable.ic_directions_form_destination_notselected; private int mIconPadding; private boolean mDialogLock; public ItineraireFragment() { super(R.layout.fragment_itineraire); mLocationProvider = NBApplication.getLocationProvider(); } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT); mIconPadding = getResources().getDimensionPixelSize(R.dimen.itinerary_icon_padding); if (savedInstanceState != null) { mFromLocation.set((Location) savedInstanceState.getParcelable(BUNDLE_LOCATION_FROM)); mToLocation.set((Location) savedInstanceState.getParcelable(BUNDLE_LOCATION_TO)); mDateTime.setMillis(savedInstanceState.getLong(BUNDLE_DATE_TIME)); mArriveBy = savedInstanceState.getBoolean(BUNDLE_ARRIVE_BY); mIconFromResId = savedInstanceState.getInt(BUNDLE_ICON_FROM); mIconToResId = savedInstanceState.getInt(BUNDLE_ICON_TO); mIconFromColor = savedInstanceState.getInt(BUNDLE_COLOR_FROM); mIconToColor = savedInstanceState.getInt(BUNDLE_COLOR_TO); final List<Parcelable> results = savedInstanceState.getParcelableArrayList(BUNDLE_RESULTS); for (final Parcelable parcelable : results) { mItineraryWrappers.add((ItineraryWrapper) parcelable); } } }; @Override public void onStart() { super.onStart(); mLocationProvider.addListener(this); } @Override public void onStop() { super.onStop(); mLocationProvider.removeListener(this); if (mAddressResolverTask != null) { mAddressResolverTask.cancel(false); } } @Override public void onSaveInstanceState(final Bundle outState) { outState.putParcelable(BUNDLE_LOCATION_FROM, mFromLocation); outState.putParcelable(BUNDLE_LOCATION_TO, mToLocation); outState.putString(BUNDLE_ADDRESS_FROM, mFromAddressTextView.getText().toString()); outState.putString(BUNDLE_ADDRESS_TO, mToAddressTextView.getText().toString()); outState.putLong(BUNDLE_DATE_TIME, mDateTime.getMillis()); outState.putBoolean(BUNDLE_ARRIVE_BY, mArriveBy); outState.putInt(BUNDLE_ICON_FROM, mIconFromResId); outState.putInt(BUNDLE_ICON_TO, mIconToResId); outState.putInt(BUNDLE_COLOR_FROM, mIconFromColor); outState.putInt(BUNDLE_COLOR_TO, mIconToColor); outState.putParcelableArrayList(BUNDLE_RESULTS, new ArrayList<ItineraryWrapper>(mItineraryWrappers)); super.onSaveInstanceState(outState); } @Override public void onListItemClick(final ListView l, final View v, final int position, final long id) { super.onListItemClick(l, v, position, id); final Bundle bundle = new Bundle(); bundle.putString(ItineraryDetailFragment.PARAM_ITINERARY_FROM, mFromAddressTextView.getText().toString()); bundle.putString(ItineraryDetailFragment.PARAM_ITINERARY_TO, mToAddressTextView.getText().toString()); bundle.putParcelable(ItineraryDetailFragment.PARAM_ITINERARY_WRAPPER, mItineraryWrappers.get(position)); final DialogFragment dialogFragment = new ItineraryDetailDialogFragment(); dialogFragment.setArguments(bundle); dialogFragment.show(getChildFragmentManager(), "ItineraryDetails"); } @Override protected void bindView(final View view, final Bundle savedInstanceState) { final ListView listView = (ListView) view.findViewById(android.R.id.list); mProgressView = view.findViewById(android.R.id.progress); mAdapter = new ItineraryWrapperArrayAdapter(getActivity(), mItineraryWrappers); listView.setAdapter(mAdapter); mIconFrom = (ImageView) view.findViewById(R.id.formIconFrom); mIconTo = (ImageView) view.findViewById(R.id.formIconTo); mFromAddressTextView = (TextView) view.findViewById(R.id.formFrom); mFromAddressTextView.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { requestFromAddress(); } }); mToAddressTextView = (TextView) view.findViewById(R.id.formTo); mToAddressTextView.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { requestToAddress(); } }); mDateAndTimeLabel = (TextView) view.findViewById(R.id.dateAndTimeLabel); mDateAndTimeLabel.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { showDatePicker(); } }); mDateKindSpinner = (Spinner) view.findViewById(R.id.dateKind); mDateKindSpinner.setAdapter(new DateKindSpinnerAdaper(getActivity())); mDateKindSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(final AdapterView<?> parent, final View view, final int position, final long id) { final boolean arriveBy = position == 1; if (arriveBy != mArriveBy) { mArriveBy = arriveBy; onFormValueChange(); } } @Override public void onNothingSelected(final AdapterView<?> parent) { } }); mReverseButton = view.findViewById(R.id.formReverse); mReverseButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { reverse(); } }); if (savedInstanceState != null) { notifyIconsChanged(); } else { final Location currentLocation = NBApplication.getLocationProvider().getLastLocation(); if (currentLocation != null && currentLocation.getLatitude() != 0 && currentLocation.getLongitude() != 0) { mFromLocation.set(currentLocation); mFromAddressTextView.setText(R.string.itineraire_current_location); mFromCurrentLocation = true; mIconFromResId = R.drawable.ic_directions_form_mylocation; notifyIconsChanged(); loadCurrentAddress(new AddressTaskListener() { @Override public void onAddressTaskResult(Address address) { if (address != null) { mFromAddressTextView.setText(FormatUtils.formatAddress(address, null)); } } @Override public void onAddressTaskPreExecute() { } }); } } setDateValue(); } @Override public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data != null) { final String address = data.getStringExtra("address"); final double latitude = data.getDoubleExtra("latitude", 0d); final double longitude = data.getDoubleExtra("longitude", 0d); final Equipement.Type type = (Type) data.getSerializableExtra("type"); final boolean currentLocation = data.getBooleanExtra("isCurrentLocation", false); if (requestCode == REQUEST_CODE_FROM) { mFromLocation.setLatitude(latitude); mFromLocation.setLongitude(longitude); mFromAddressTextView.setText(address); if (type == null) { mIconFromResId = currentLocation ? R.drawable.ic_action_locate_blue : R.drawable.ic_directions_form_destination_notselected; mIconFromColor = Color.TRANSPARENT; } else { mIconFromResId = type.getDrawableRes(); mIconFromColor = getActivity().getResources().getColor(type.getBackgroundColorRes()); } mFromCurrentLocation = currentLocation; } else { mToLocation.setLatitude(latitude); mToLocation.setLongitude(longitude); mToAddressTextView.setText(address); if (type == null) { mIconFromResId = currentLocation ? R.drawable.ic_action_locate_blue : R.drawable.ic_directions_form_destination_notselected; mIconToColor = Color.TRANSPARENT; } else { mIconToResId = type.getDrawableRes(); mIconToColor = getActivity().getResources().getColor(type.getBackgroundColorRes()); } mToCurrentLocation = currentLocation; } notifyIconsChanged(); } onFormValueChange(); } private void requestFromAddress() { requestAddress(REQUEST_CODE_FROM, mFromCurrentLocation ? null : mFromAddressTextView.getText()); } private void requestToAddress() { requestAddress(REQUEST_CODE_TO, mToCurrentLocation ? null : mToAddressTextView.getText()); } private void requestAddress(final int requestCode, final CharSequence query) { final Intent intent = new Intent(getActivity(), AddressSearchActivity.class); if (query != null) { intent.putExtra(AddressSearchFragment.PARAM_QUERY, query); } startActivityForResult(intent, requestCode); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) getActivity().overridePendingTransition(R.anim.slide_in_from_right, R.anim.half_fade_out); } private void showDatePicker() { final DatePickerDialog dialog = new DatePickerDialog(getActivity(), this, mDateTime.getYear(), mDateTime.getMonthOfYear() - 1, mDateTime.getDayOfMonth()); dialog.show(); } private void showTimePicker() { final TimePickerDialog dialog = new TimePickerDialog(getActivity(), this, mDateTime.getHourOfDay(), mDateTime.getMinuteOfHour(), true); dialog.show(); } private void reverse() { final CharSequence fromText = mFromAddressTextView.getText(); final CharSequence toText = mToAddressTextView.getText(); final Location temp = new Location(LocationManager.GPS_PROVIDER); mToAddressTextView.setText(fromText); mFromAddressTextView.setText(toText); temp.set(mFromLocation); mFromLocation.set(mToLocation); mToLocation.set(temp); int t = mIconToResId; mIconToResId = mIconFromResId; mIconFromResId = t; t = mIconFromColor; mIconFromColor = mIconToColor; mIconToColor = t; boolean tempB = mFromCurrentLocation; mFromCurrentLocation = mToCurrentLocation; mToCurrentLocation = tempB; notifyIconsChanged(); onFormValueChange(); final Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.rotation_full); mReverseButton.startAnimation(animation); } private void notifyIconsChanged() { setIcon(mIconFrom, mIconFromResId, mIconFromColor); setIcon(mIconTo, mIconToResId, mIconToColor); } private void setIcon(final ImageView imageView, final int icoRes, final int color) { imageView.setImageResource(icoRes); if (color == Color.TRANSPARENT) { imageView.setBackgroundDrawable(null); imageView.setPadding(0, 0, 0, 0); } else { imageView.setBackgroundDrawable(ColorUtils.getCircle(color)); imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding); } } @Override public void onDateSet(final DatePicker view, final int year, final int monthOfYear, final int dayOfMonth) { mDateTime.setYear(year); mDateTime.setMonthOfYear(monthOfYear + 1); mDateTime.setDayOfMonth(dayOfMonth); if (!mDialogLock) { showTimePicker(); mDialogLock = true; } } @Override public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute) { mDateTime.setHourOfDay(hourOfDay); mDateTime.setMinuteOfHour(minute); mDateAndTimeLabel.setText(mDateFormat.format(mDateTime.toDate())); onFormValueChange(); mDialogLock = false; } private void onFormValueChange() { final boolean hasLocations = mFromLocation.getLatitude() != 0 && mToLocation.getLatitude() != 0; if (hasLocations) { showProgress(); sendRequest(); } } private void setDateValue() { mDateAndTimeLabel.setText(mDateFormat.format(mDateTime.toDate())); } private void sendRequest() { if (mFromLocation.bearingTo(mToLocation) == 0.0f) { hideProgress(); mItineraryWrappers.add(ItineraryWrapper.getUnicornItinerary()); mAdapter.notifyDataSetChanged(); } else { final Bundle bundle = new Bundle(); bundle.putDouble(ItineraryLoader.PARAM_FROM_LATITUDE, mFromLocation.getLatitude()); bundle.putDouble(ItineraryLoader.PARAM_FROM_LONGITUDE, mFromLocation.getLongitude()); bundle.putDouble(ItineraryLoader.PARAM_TO_LATITUDE, mToLocation.getLatitude()); bundle.putDouble(ItineraryLoader.PARAM_TO_LONGITUDE, mToLocation.getLongitude()); bundle.putLong(ItineraryLoader.PARAM_TIME, mDateTime.getMillis()); bundle.putBoolean(ItineraryLoader.PARAM_ARRIVE_BY, mArriveBy); getLoaderManager().restartLoader(0, bundle, this); } } private void showProgress() { mProgressView.setVisibility(View.VISIBLE); mAdapter.clear(); } private void hideProgress() { mProgressView.setVisibility(View.GONE); } private void loadCurrentAddress(AddressTaskListener listener) { if (mAddressResolverTask != null) { mAddressResolverTask.cancel(true); } mAddressResolverTask = (AddressResolverTask) new AddressResolverTask(listener).execute(); } @Override public Loader<AsyncResult<List<ItineraryWrapper>>> onCreateLoader(final int loaderId, final Bundle bundle) { return new ItineraryLoader(getActivity(), bundle); } @Override public void onLoadFinished(final Loader<AsyncResult<List<ItineraryWrapper>>> loader, final AsyncResult<List<ItineraryWrapper>> result) { hideProgress(); if (result.getException() == null) { mItineraryWrappers.clear(); if (result.getResult() == null) { mItineraryWrappers.add(ItineraryWrapper.getEmptyItinerary()); } else { mItineraryWrappers.addAll(result.getResult()); } } else { mItineraryWrappers.add(ItineraryWrapper.getErrorItinerary()); BugSenseHandler.sendExceptionMessage("Erreur de calcul d'itinéraire.", null, result.getException()); Log.e(TAG, "Erreur de calcul d'itinéraire.", result.getException()); } mAdapter.notifyDataSetChanged(); getListView().smoothScrollToPosition(1); } @Override public void onLoaderReset(final Loader<AsyncResult<List<ItineraryWrapper>>> arg0) { } @Override public void onConnecting() { } @Override public void onLocationChanged(Location location) { } @Override public void onDisconnected() { } @Override public void onLocationTimeout() { } }