/**
* 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.List;
import java.util.concurrent.atomic.AtomicBoolean;
import net.naonedbus.BuildConfig;
import net.naonedbus.R;
import net.naonedbus.activity.impl.AddEventActivity;
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.Favori;
import net.naonedbus.bean.Ligne;
import net.naonedbus.bean.Sens;
import net.naonedbus.bean.async.AsyncResult;
import net.naonedbus.bean.horaire.EmptyHoraire;
import net.naonedbus.bean.horaire.Horaire;
import net.naonedbus.fragment.CustomInfiniteListFragement;
import net.naonedbus.intent.ParamIntent;
import net.naonedbus.manager.impl.ArretManager;
import net.naonedbus.manager.impl.FavoriManager;
import net.naonedbus.manager.impl.HoraireManager;
import net.naonedbus.manager.impl.SensManager;
import net.naonedbus.utils.FormatUtils;
import net.naonedbus.widget.adapter.impl.HoraireArrayAdapter;
import net.naonedbus.widget.indexer.impl.HoraireIndexer;
import org.joda.time.DateMidnight;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Events;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.DatePicker;
import android.widget.ListAdapter;
import android.widget.Toast;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
public class HorairesFragment extends CustomInfiniteListFragement implements OnItemClickListener {
private static final String LOG_TAG = "HorairesFragment";
private static final boolean DBG = BuildConfig.DEBUG;
public static final String PARAM_LIGNE = "ligne";
public static final String PARAM_SENS = "sens";
public static final String PARAM_ARRET = "arret";
public static interface OnSensChangeListener {
void onSensChange(Sens newSens);
}
private final static IntentFilter intentFilter;
static {
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_TICK);
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
intentFilter.addAction(Intent.ACTION_TIME_CHANGED);
}
/**
* Reçoit les intents de notre intentFilter
*/
private final BroadcastReceiver intentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
updateItemsTime();
}
};
private final HoraireManager mHoraireManager;
private final ArretManager mArretManager;
private final SensManager mSensManager;
private final FavoriManager mFavoriManager;
private OnSensChangeListener mOnSensChangeListener;
private HoraireArrayAdapter mAdapter;
private final List<Horaire> mHoraires;
private Ligne mLigne;
private Sens mSens;
private Arret mArret;
private boolean mIsFirstLoad = true;
private final AtomicBoolean mIsLoading = new AtomicBoolean(false);
private DateMidnight mLastDayLoaded;
private DateMidnight mDayToLoad;
private DateTime mLastDateTimeLoaded;
private final DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(final DatePicker view, final int year, final int monthOfYear, final int dayOfMonth) {
changeDate(new DateMidnight(year, monthOfYear + 1, dayOfMonth));
}
};
public HorairesFragment() {
super(R.layout.fragment_listview_section);
mHoraireManager = HoraireManager.getInstance();
mFavoriManager = FavoriManager.getInstance();
mArretManager = ArretManager.getInstance();
mSensManager = SensManager.getInstance();
mHoraires = new ArrayList<Horaire>();
mLastDayLoaded = new DateMidnight();
mDayToLoad = new DateMidnight();
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Activity activity = getActivity();
if (activity instanceof OnSensChangeListener) {
setOnChangeSensListener((OnSensChangeListener) activity);
} else {
throw new IllegalStateException();
}
mLigne = getArguments().getParcelable(PARAM_LIGNE);
mSens = getArguments().getParcelable(PARAM_SENS);
mArret = getArguments().getParcelable(PARAM_ARRET);
setHasOptionsMenu(true);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mAdapter = new HoraireArrayAdapter(getActivity(), mHoraires);
mAdapter.setIndexer(new HoraireIndexer());
getListView().setOnItemClickListener(this);
loadContent();
}
@Override
public void onStart() {
getActivity().registerReceiver(intentReceiver, intentFilter);
super.onStart();
}
@Override
public void onStop() {
getActivity().unregisterReceiver(intentReceiver);
super.onDestroy();
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id) {
final Horaire horaire = (Horaire) getListView().getItemAtPosition(position);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
final String title = mArret.getNomArret();
final String description = FormatUtils.formatTitle(
getString(R.string.dialog_title_menu_lignes, mLigne.getCode()), mArret.getNomArret(), mSens.text);
final Intent calIntent = new Intent(Intent.ACTION_INSERT);
calIntent.setType("vnd.android.cursor.item/event");
calIntent.putExtra(Events.TITLE, title);
calIntent.putExtra(Events.DESCRIPTION, description);
calIntent.putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, false);
calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, horaire.getHoraire().getMillis());
calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, horaire.getHoraire().getMillis());
try {
startActivity(calIntent);
getActivity().overridePendingTransition(R.anim.slide_in_from_right, R.anim.half_fade_out);
} catch (ActivityNotFoundException e) {
addEventFallback(horaire);
}
} else {
addEventFallback(horaire);
}
}
private void addEventFallback(Horaire horaire) {
final ParamIntent intent = new ParamIntent(getActivity(), AddEventActivity.class);
intent.putExtra(AddEventActivity.PARAM_LIGNE, mLigne);
intent.putExtra(AddEventActivity.PARAM_SENS, mSens);
intent.putExtra(AddEventActivity.PARAM_ARRET, mArret);
intent.putExtra(AddEventActivity.PARAM_TIMESTAMP, horaire.getHoraire().getMillis());
startActivity(intent);
getActivity().overridePendingTransition(R.anim.slide_in_from_right, R.anim.half_fade_out);
}
@Override
protected void onLoadMoreItems() {
if (mLastDayLoaded != null) {
loadHoraires(mLastDayLoaded.plusDays(1));
}
}
private void loadHoraires(final DateMidnight date) {
if (mIsLoading.get() == false) {
if (DBG)
Log.d(LOG_TAG, "loadHoraires " + date.toString() + "\t" + mIsLoading.get());
mDayToLoad = date;
refreshContent();
}
}
@Override
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.fragment_horaires, menu);
final MenuItem menuFavori = menu.findItem(R.id.menu_favori);
final int icon = isFavori() ? R.drawable.ic_star : R.drawable.ic_star_outline;
menuFavori.setIcon(icon);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public void onPrepareOptionsMenu(final Menu menu) {
final MenuItem menuFavori = menu.findItem(R.id.menu_favori);
final int icon = isFavori() ? R.drawable.ic_star : R.drawable.ic_star_outline;
menuFavori.setIcon(icon);
super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_favori:
onStarClick();
break;
case R.id.menu_place:
showArretPlan();
break;
case R.id.menu_date:
changeDate();
break;
case R.id.menu_date_maintenant:
changeDateToNow();
break;
case R.id.menu_comment:
menuComment();
break;
case R.id.menu_show_plan:
menuShowPlan();
break;
case R.id.menu_sens:
menuChangeSens();
break;
default:
break;
}
return false;
}
private boolean isFavori() {
final Favori item = mFavoriManager.getSingle(getActivity().getContentResolver(), mArret.getId());
return (item != null);
}
@SuppressLint("NewApi")
private void onStarClick() {
if (isFavori()) {
removeFromFavoris();
Toast.makeText(getActivity(), R.string.toast_favori_retire, Toast.LENGTH_SHORT).show();
} else {
addToFavoris();
Toast.makeText(getActivity(), R.string.toast_favori_ajout, Toast.LENGTH_SHORT).show();
}
getSherlockActivity().invalidateOptionsMenu();
}
private void changeDate() {
final DatePickerDialog dateDialog = new DatePickerDialog(getActivity(), mDateSetListener,
mLastDayLoaded.getYear(), mLastDayLoaded.getMonthOfYear() - 1, mLastDayLoaded.getDayOfMonth());
dateDialog.show();
}
private void changeDateToNow() {
changeDate(new DateMidnight());
}
private void addToFavoris() {
mFavoriManager.addFavori(getActivity().getContentResolver(), mArret);
}
private void removeFromFavoris() {
mFavoriManager.removeFavori(getActivity().getContentResolver(), mArret.getId());
}
protected void showArretPlan() {
final Intent intent = new Intent(getActivity(), MapActivity.class);
intent.putExtra(MapFragment.PARAM_ITEM_ID, mArret.getIdStation());
intent.putExtra(MapFragment.PARAM_ITEM_TYPE, TypeOverlayItem.TYPE_STATION.getId());
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);
intent.putExtra(CommentaireActivity.PARAM_ARRET, mArret);
startActivity(intent);
}
private void menuShowPlan() {
final Intent intent = new Intent(getActivity(), PlanActivity.class);
intent.putExtra(PlanActivity.PARAM_CODE_LIGNE, mLigne.getCode());
startActivity(intent);
}
@SuppressLint("NewApi")
private void menuChangeSens() {
Sens autreSens = null;
// Inverser le sens
final List<Sens> sens = mSensManager.getAll(getActivity().getContentResolver(), mLigne.getCode());
for (final Sens sensItem : sens) {
if (sensItem._id != mSens._id) {
autreSens = sensItem;
break;
}
}
if (autreSens != null) {
// Chercher l'arrêt dans le nouveau sens
final Arret arret = mArretManager.getSingle(getActivity().getContentResolver(), mLigne.getCode(),
autreSens.code, mArret.getNormalizedNom());
if (arret != null) {
mSens = autreSens;
mArret = arret;
mAdapter.clear();
mAdapter.notifyDataSetChanged();
changeDateToNow();
if (mOnSensChangeListener != null) {
mOnSensChangeListener.onSensChange(mSens);
}
getSherlockActivity().invalidateOptionsMenu();
return;
}
}
Toast.makeText(getActivity(), getString(R.string.toast_autre_sens), Toast.LENGTH_SHORT).show();
}
/**
* Clear data and reload with a new date
*
* @param date
*/
public void changeDate(final DateMidnight date) {
mAdapter.clear();
mIsFirstLoad = true;
mLastDateTimeLoaded = null;
setListAdapter(null);
loadHoraires(date);
}
@Override
protected AsyncResult<ListAdapter> loadContent(final Context context, final Bundle bundle) {
mIsLoading.set(true);
final AsyncResult<ListAdapter> result = new AsyncResult<ListAdapter>();
if (DBG)
Log.d(LOG_TAG, "\tloadContent " + mDayToLoad.toString());
try {
final List<Horaire> data = mHoraireManager.getSchedules(context.getContentResolver(), mArret, mDayToLoad,
mLastDateTimeLoaded);
if (data.size() == 0) {
// Si le précédent chargement à déjà charger la totalité du jour
// actuel (ligne de nuit par exemple) ne pas réafficher le jour.
if (((mLastDayLoaded == null || mLastDateTimeLoaded == null) || !mLastDayLoaded
.equals(mLastDateTimeLoaded.toDateMidnight()))) {
mHoraires.add(new EmptyHoraire(R.string.msg_nothing_horaires, mLastDayLoaded));
}
} else {
mHoraires.addAll(data);
final Horaire lastHoraire = mHoraires.get(mHoraires.size() - 1);
mLastDateTimeLoaded = lastHoraire.getHoraire();
}
mLastDayLoaded = new DateMidnight(mDayToLoad);
result.setResult(mAdapter);
} catch (final Exception e) {
result.setException(e);
}
return result;
}
@Override
protected void onPostExecute() {
super.onPostExecute();
mAdapter.notifyDataSetChanged();
updateItemsTime();
if (DBG)
Log.d(LOG_TAG, "\tloadContent end " + mLastDayLoaded.toString());
mIsLoading.set(false);
}
/**
* Mettre à jour les informations de délais
*/
private void updateItemsTime() {
final Long currentTime = new DateTime().minusMinutes(5).withSecondOfMinute(0).withMillisOfSecond(0).getMillis();
final DateTime now = new DateTime().withSecondOfMinute(0).withMillisOfSecond(0);
int nextHorairePosition = -1;
int delay;
DateTime itemDateTime;
Horaire horaire;
for (int i = 0; i < mAdapter.getCount(); i++) {
horaire = mAdapter.getItem(i);
if (horaire instanceof EmptyHoraire) {
continue;
}
itemDateTime = new DateTime(horaire.getHoraire()).withSecondOfMinute(0).withMillisOfSecond(0);
delay = Minutes.minutesBetween(now, itemDateTime).getMinutes();
if (delay > 0 && delay < 60) {
horaire.setDelai(getString(R.string.msg_depart_min, delay));
} else if (delay == 0) {
horaire.setDelai(getString(R.string.msg_depart_proche));
} else {
horaire.setDelai(null);
}
horaire.setBeforeNow(itemDateTime.isBefore(now));
// Recherche le prochain horaire
if (nextHorairePosition == -1 && (horaire.getHoraire().isAfterNow() || horaire.getHoraire().isEqualNow())) {
nextHorairePosition = mAdapter.getPosition(horaire);
}
}
// Si aucun prochain horaire n'est trouvé, sélectionner le dernier
if (nextHorairePosition == -1) {
nextHorairePosition = mAdapter.getCount() - 1;
}
mAdapter.notifyDataSetChanged();
if (nextHorairePosition != -1 && mIsFirstLoad) {
getListView().setSelection(nextHorairePosition);
mIsFirstLoad = false;
}
}
public void setOnChangeSensListener(final OnSensChangeListener l) {
mOnSensChangeListener = l;
}
}