/**
* 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.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import net.naonedbus.NBApplication;
import net.naonedbus.R;
import net.naonedbus.activity.impl.ArretDetailActivity;
import net.naonedbus.activity.impl.ArretsActivity.OnChangeSens;
import net.naonedbus.activity.impl.CommentaireActivity;
import net.naonedbus.activity.impl.MapActivity;
import net.naonedbus.activity.impl.PlanActivity;
import net.naonedbus.activity.map.overlay.TypeOverlayItem;
import net.naonedbus.bean.Arret;
import net.naonedbus.bean.Ligne;
import net.naonedbus.bean.Sens;
import net.naonedbus.bean.async.AsyncResult;
import net.naonedbus.comparator.ArretComparator;
import net.naonedbus.comparator.ArretOrdreComparator;
import net.naonedbus.fragment.CustomListFragment;
import net.naonedbus.helper.StateHelper;
import net.naonedbus.manager.impl.ArretManager;
import net.naonedbus.manager.impl.FavoriManager;
import net.naonedbus.provider.impl.NaoLocationManager;
import net.naonedbus.provider.impl.NaoLocationManager.NaoLocationListener;
import net.naonedbus.utils.InfoDialogUtils;
import net.naonedbus.widget.adapter.impl.ArretArrayAdapter;
import net.naonedbus.widget.adapter.impl.ArretArrayAdapter.ViewType;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
public class ArretsFragment extends CustomListFragment implements OnChangeSens, NaoLocationListener {
public static final String PARAM_LIGNE = "ligne";
private final static int SORT_NOM = 0;
private final static int SORT_ORDRE = 1;
private final static int FILTER_ALL = 2;
private final static int FILTER_FAVORIS = 3;
private final static SparseIntArray MENU_MAPPING = new SparseIntArray();
static {
MENU_MAPPING.append(SORT_NOM, R.id.menu_sort_name);
MENU_MAPPING.append(SORT_ORDRE, R.id.menu_sort_ordre);
MENU_MAPPING.append(FILTER_ALL, R.id.menu_filter_all);
MENU_MAPPING.append(FILTER_FAVORIS, R.id.menu_filter_favoris);
}
private interface DistanceTaskCallback {
void onNearestStationFound(Integer position);
void onPostExecute();
}
protected final SparseArray<Comparator<Arret>> mComparators;
protected int mCurrentSort;
private final FavoriManager mFavoriManager;
private StateHelper mStateHelper;
private final NaoLocationManager mLocationProvider;
private DistanceTask mDistanceTask;
private DistanceTaskCallback mDistanceTaskCallback;
private Integer mNearestArretPosition;
private ArretArrayAdapter mAdapter;
private int mCurrentFilter = FILTER_ALL;
private int mDefaultDividerHeight;
private List<Arret> mArrets;
private Ligne mLigne;
private Sens mSens;
public ArretsFragment() {
super(R.layout.fragment_listview);
mLocationProvider = NBApplication.getLocationProvider();
mLocationProvider.addListener(this);
mComparators = new SparseArray<Comparator<Arret>>();
mComparators.append(SORT_NOM, new ArretComparator());
mComparators.append(SORT_ORDRE, new ArretOrdreComparator());
mFavoriManager = FavoriManager.getInstance();
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
registerForContextMenu(getListView());
mDefaultDividerHeight = getListView().getDividerHeight();
mLigne = getArguments().getParcelable(PARAM_LIGNE);
mStateHelper = new StateHelper(getActivity());
mCurrentSort = mStateHelper.getSortType(this, SORT_NOM);
setCurrentFilter(mStateHelper.getFilterType(this, FILTER_ALL));
mArrets = new ArrayList<Arret>();
mAdapter = new ArretArrayAdapter(getActivity(), mArrets);
mAdapter.setRouteColor(mLigne.getCouleur());
if (mCurrentSort == SORT_ORDRE) {
mAdapter.setViewType(ViewType.TYPE_METRO);
getListView().setDividerHeight(0);
}
mDistanceTaskCallback = new DistanceTaskCallback() {
@Override
public void onNearestStationFound(final Integer position) {
mNearestArretPosition = position;
final ArretArrayAdapter adapter = (ArretArrayAdapter) getListAdapter();
if (adapter != null && mNearestArretPosition != null) {
adapter.setNearestPosition(mNearestArretPosition);
adapter.notifyDataSetChanged();
}
}
@Override
public void onPostExecute() {
final ArretArrayAdapter adapter = (ArretArrayAdapter) getListAdapter();
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
};
}
@Override
public void onStart() {
super.onStart();
mLocationProvider.addListener(this);
}
@Override
public void onStop() {
super.onStop();
// Save state
mStateHelper.setSortType(this, mCurrentSort);
mStateHelper.setFilterType(this, mCurrentFilter);
mLocationProvider.removeListener(this);
if (mDistanceTask != null) {
mDistanceTask.cancel(true);
}
}
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.fragment_arrets, menu);
menu.findItem(MENU_MAPPING.get(mCurrentSort)).setChecked(true);
menu.findItem(MENU_MAPPING.get(mCurrentFilter)).setChecked(true);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onPrepareOptionsMenu(final Menu menu) {
menu.findItem(R.id.menu_location).setVisible(mLocationProvider.isEnabled());
super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_sort_name:
item.setChecked(true);
changeSortOrder(SORT_NOM, ViewType.TYPE_STANDARD);
break;
case R.id.menu_sort_ordre:
item.setChecked(true);
changeSortOrder(SORT_ORDRE, ViewType.TYPE_METRO);
break;
case R.id.menu_filter_all:
if (mCurrentFilter != FILTER_ALL) {
item.setChecked(true);
setCurrentFilter(FILTER_ALL);
refreshContent();
}
break;
case R.id.menu_filter_favoris:
if (mCurrentFilter != FILTER_FAVORIS) {
item.setChecked(true);
setCurrentFilter(FILTER_FAVORIS);
refreshContent();
}
break;
case R.id.menu_show_plan:
menuShowPlan();
break;
case R.id.menu_location:
menuLocation();
break;
case R.id.menu_comment:
menuComment();
break;
}
return false;
}
@Override
public void onCreateContextMenu(final ContextMenu menu, final View v, final ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
final AdapterView.AdapterContextMenuInfo cmi = (AdapterView.AdapterContextMenuInfo) menuInfo;
final Arret arret = (Arret) getListView().getItemAtPosition(cmi.position);
final android.view.MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.fragment_arrets_contextual, menu);
menu.setHeaderTitle(arret.getNomArret());
final android.view.MenuItem menuFavori = menu.findItem(R.id.menu_favori);
if (mFavoriManager.isFavori(getActivity().getContentResolver(), arret.getId())) {
menuFavori.setTitle(R.string.action_favori_remove);
} else {
menuFavori.setTitle(R.string.action_favori_add);
}
}
@Override
public boolean onContextItemSelected(final android.view.MenuItem item) {
final AdapterView.AdapterContextMenuInfo cmi = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final Arret arret = (Arret) getListView().getItemAtPosition(cmi.position);
switch (item.getItemId()) {
case R.id.menu_show_plan:
menuShowMap(arret);
break;
case R.id.menu_favori:
if (mFavoriManager.isFavori(getActivity().getContentResolver(), arret.getId())) {
removeFromFavoris(arret);
} else {
addToFavoris(arret);
}
break;
case R.id.menu_comment:
menuComment(arret);
break;
default:
break;
}
return true;
}
@Override
public void onListItemClick(final ListView l, final View v, final int position, final long id) {
super.onListItemClick(l, v, position, id);
final Arret arret = (Arret) l.getItemAtPosition(position);
final Intent intent = new Intent(getActivity(), ArretDetailActivity.class);
intent.putExtra(ArretDetailActivity.PARAM_LIGNE, mLigne);
intent.putExtra(ArretDetailActivity.PARAM_SENS, mSens);
intent.putExtra(ArretDetailActivity.PARAM_ARRET, arret);
startActivity(intent);
}
private void menuShowPlan() {
final Intent intent = new Intent(getActivity(), PlanActivity.class);
intent.putExtra(PlanActivity.PARAM_CODE_LIGNE, mLigne.getCode());
startActivity(intent);
}
private void menuComment() {
final Intent intent = new Intent(getActivity(), CommentaireActivity.class);
intent.putExtra(CommentaireActivity.PARAM_LIGNE, mLigne);
intent.putExtra(CommentaireActivity.PARAM_SENS, mSens);
startActivity(intent);
}
private void menuComment(final Arret arret) {
final Intent intent = new Intent(getActivity(), CommentaireActivity.class);
intent.putExtra(CommentaireActivity.PARAM_LIGNE, mLigne);
intent.putExtra(CommentaireActivity.PARAM_SENS, mSens);
intent.putExtra(CommentaireActivity.PARAM_ARRET, arret);
startActivity(intent);
}
private void addToFavoris(final Arret arret) {
mFavoriManager.addFavori(getActivity().getContentResolver(), arret);
Toast.makeText(getActivity(), R.string.toast_favori_ajout, Toast.LENGTH_SHORT).show();
}
private void removeFromFavoris(final Arret arret) {
mFavoriManager.removeFavori(getActivity().getContentResolver(), arret.getId());
Toast.makeText(getActivity(), R.string.toast_favori_retire, Toast.LENGTH_SHORT).show();
}
private void menuShowMap(final Arret arret) {
final Intent intent = new Intent(getActivity(), MapActivity.class);
intent.putExtra(MapFragment.PARAM_ITEM_ID, arret.getIdStation());
intent.putExtra(MapFragment.PARAM_ITEM_TYPE, TypeOverlayItem.TYPE_STATION.getId());
startActivity(intent);
}
@TargetApi(11)
private void menuLocation() {
if (mNearestArretPosition != null) {
final ListView listView = getListView();
final int listViewHeight = listView.getHeight();
final int itemHeight = listView.getChildAt(0).getHeight();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
listView.setSelectionFromTop(mNearestArretPosition, (listViewHeight - itemHeight) / 2);
} else {
listView.smoothScrollToPositionFromTop(mNearestArretPosition, (listViewHeight - itemHeight) / 2);
}
}
}
/**
* Définir le filtre courant.
*
* @param filter
*/
private void setCurrentFilter(final int filter) {
mCurrentFilter = filter;
if (mCurrentFilter == FILTER_ALL) {
setEmptyMessageValues(R.string.error_title_empty, R.string.error_summary_empty, R.drawable.ic_sad_face);
} else {
setEmptyMessageValues(R.string.error_title_empty_favori, R.string.error_summary_empty_arrets_favoris,
R.drawable.ic_star_empty);
}
}
/**
* Changer l'ordre de tri des arrêts.
*
* @param sortOrder
* L'id du comparator
* @param viewType
* Le type de vue de l'adapter
*/
private void changeSortOrder(final int sortOrder, final ViewType viewType) {
final ArretArrayAdapter adapter = (ArretArrayAdapter) getListAdapter();
mCurrentSort = sortOrder;
adapter.setViewType(viewType);
getListView().setSelection(0);
sort();
loadDistances();
if (viewType == ViewType.TYPE_METRO) {
getListView().setDividerHeight(0);
InfoDialogUtils.showIfNecessary(getActivity(), R.string.dialog_title_arret_order,
R.string.dialog_content_arret_order);
} else {
getListView().setDividerHeight(mDefaultDividerHeight);
}
}
/**
* Trier les parkings selon les préférences.
*/
private void sort() {
final ArretArrayAdapter adapter = (ArretArrayAdapter) getListAdapter();
if (adapter != null) {
sort(adapter);
adapter.notifyDataSetChanged();
}
}
/**
* Trier les parkings selon les préférences.
*
* @param adapter
*/
private void sort(final ArretArrayAdapter adapter) {
final Comparator<Arret> comparator = mComparators.get(mCurrentSort);
if (comparator != null) {
adapter.sort(comparator);
}
}
@Override
protected AsyncResult<ListAdapter> loadContent(final Context context, final Bundle bundle) {
final AsyncResult<ListAdapter> result = new AsyncResult<ListAdapter>();
try {
final ArretManager arretManager = ArretManager.getInstance();
final List<Arret> arrets;
if (mCurrentFilter == FILTER_ALL) {
arrets = arretManager.getAll(context.getContentResolver(), mSens.codeLigne, mSens.code);
} else {
arrets = arretManager.getArretsFavoris(context.getContentResolver(), mSens.codeLigne, mSens.code);
}
mArrets.clear();
mArrets.addAll(arrets);
result.setResult(mAdapter);
} catch (final Exception e) {
result.setException(e);
}
return result;
}
@Override
protected void onPostExecute() {
sort();
loadDistances();
mAdapter.notifyDataSetChanged();
}
@Override
public void onChangeSens(final Sens sens) {
if (sens.equals(mSens) == false) {
mSens = sens;
refreshContent();
}
}
/**
* Lancer le calcul des distances.
*/
private void loadDistances() {
if (mDistanceTask != null) {
mDistanceTask.cancel(true);
}
if (getListAdapter() != null) {
mDistanceTask = (DistanceTask) new DistanceTask(mDistanceTaskCallback, mLocationProvider.getLastLocation(),
getListAdapter()).execute();
}
}
/**
* Classe de calcul de la distance des arrêts.
*/
private class DistanceTask extends AsyncTask<Void, Void, Integer> {
private DistanceTaskCallback mCallback;
private final ListAdapter mAdapter;
private final Location mCurrentLocation;
public DistanceTask(final DistanceTaskCallback callback, final Location currentLocation,
final ListAdapter adapter) {
mCallback = callback;
mCurrentLocation = currentLocation;
mAdapter = adapter;
}
@Override
protected Integer doInBackground(final Void... params) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
Arret arret;
Integer nearestPosition = null;
Float nearestDistance = Float.MAX_VALUE;
Float distance = null;
final Location equipementLocation = new Location(LocationManager.GPS_PROVIDER);
if (mCurrentLocation != null) {
for (int i = 0; i < mAdapter.getCount(); i++) {
arret = (Arret) mAdapter.getItem(i);
equipementLocation.setLatitude(arret.getLatitude());
equipementLocation.setLongitude(arret.getLongitude());
distance = mCurrentLocation.distanceTo(equipementLocation);
arret.setDistance(distance);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPosition = i;
}
if (isCancelled()) {
break;
}
}
}
return nearestPosition;
}
@Override
protected void onCancelled() {
super.onCancelled();
mCallback = null;
}
@Override
protected void onPostExecute(final Integer result) {
if (!isCancelled() && mCallback != null) {
mCallback.onPostExecute();
mCallback.onNearestStationFound(result);
}
}
}
@Override
public void onLocationChanged(final Location location) {
loadDistances();
}
@Override
public void onConnecting() {
}
@Override
public void onDisconnected() {
}
@Override
public void onLocationTimeout() {
}
}