/**
* Copyright (C) 2013 - 2015 the enviroCar community
*
* This file is part of the enviroCar app.
*
* The enviroCar app 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.
*
* The enviroCar app 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 the enviroCar app. If not, see http://www.gnu.org/licenses/.
*/
package org.envirocar.app.view.tracklist;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.AsyncTask;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;
import com.github.jorgecastilloprz.FABProgressCircle;
import com.mapbox.mapboxsdk.geometry.BoundingBox;
import com.mapbox.mapboxsdk.tileprovider.tilesource.WebSourceTileLayer;
import com.mapbox.mapboxsdk.views.MapView;
import org.envirocar.app.R;
import org.envirocar.app.view.trackdetails.TrackSpeedMapOverlay;
import org.envirocar.app.view.utils.MapUtils;
import org.envirocar.core.entity.Track;
import org.envirocar.core.exception.NoMeasurementsException;
import org.envirocar.core.logging.Logger;
import org.envirocar.core.trackprocessing.TrackStatisticsProvider;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import butterknife.ButterKnife;
import butterknife.InjectView;
import rx.Scheduler;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action0;
/**
* TODO JavaDoc
*
* @author dewall
*/
public abstract class AbstractTrackListCardAdapter<E extends
AbstractTrackListCardAdapter
.TrackCardViewHolder> extends RecyclerView.Adapter<E> {
private static final Logger LOG = Logger.getLogger(AbstractTrackListCardAdapter.class);
protected static final DecimalFormat DECIMAL_FORMATTER_TWO = new DecimalFormat("#.##");
protected static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance();
protected static final DateFormat UTC_DATE_FORMATTER = new SimpleDateFormat("HH:mm:ss", Locale
.ENGLISH);
static {
UTC_DATE_FORMATTER.setTimeZone(TimeZone.getTimeZone("UTC"));
}
protected final List<Track> mTrackDataset;
protected Scheduler.Worker mMainThreadWorker = AndroidSchedulers.mainThread().createWorker();
protected final OnTrackInteractionCallback mTrackInteractionCallback;
/**
* Constructor.
*
* @param tracks the list of tracks to show cards for.
*/
public AbstractTrackListCardAdapter(List<Track> tracks, final OnTrackInteractionCallback
callback) {
this.mTrackDataset = tracks;
this.mTrackInteractionCallback = callback;
}
@Override
public int getItemCount() {
return mTrackDataset.size();
}
/**
* Adds a track to the dataset.
*
* @param track the track to insert.
*/
public void addItem(Track track) {
mTrackDataset.add(track);
notifyDataSetChanged();
}
/**
* Removes a track from the dataset.
*
* @param track the track to remove.
*/
public void removeItem(Track track) {
if (mTrackDataset.contains(track)) {
mTrackDataset.remove(track);
notifyDataSetChanged();
}
}
protected void bindLocalTrackViewHolder(TrackCardViewHolder holder, Track track) {
holder.mDistance.setText("...");
holder.mDuration.setText("...");
// First, load the track from the dataset
holder.mTitleTextView.setText(track.getName());
// Initialize the mapView.
initMapView(holder, track);
// Set all the view parameters.
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// Set the duration text.
try {
String date = UTC_DATE_FORMATTER.format(new Date(
track.getDuration()));
mMainThreadWorker.schedule(new Action0() {
@Override
public void call() {
holder.mDuration.setText(date);
}
});
// Set the tracklength parameter.
double distanceOfTrack = ((TrackStatisticsProvider) track).getDistanceOfTrack();
String tracklength = String.format("%s km", DECIMAL_FORMATTER_TWO.format(
distanceOfTrack));
mMainThreadWorker.schedule(new Action0() {
@Override
public void call() {
holder.mDistance.setText(tracklength);
}
});
} catch (NoMeasurementsException e) {
LOG.warn(e.getMessage(), e);
mMainThreadWorker.schedule(new Action0() {
@Override
public void call() {
holder.mDistance.setText("0 km");
holder.mDuration.setText("0:00");
}
});
}
return null;
}
}.execute();
// if the menu is not already inflated, then..
if (!holder.mToolbar.getMenu().hasVisibleItems()) {
// Inflate the menu and set an appropriate OnMenuItemClickListener.
holder.mToolbar.inflateMenu(R.menu.menu_tracklist_cardlayout);
if (track.isRemoteTrack()) {
holder.mToolbar.getMenu().removeItem(R.id.menu_tracklist_cardlayout_item_upload);
}
}
holder.mToolbar.setOnMenuItemClickListener(item -> {
LOG.info("Item clicked for track " + track.getTrackID());
switch (item.getItemId()) {
case R.id.menu_tracklist_cardlayout_item_details:
mTrackInteractionCallback.onTrackDetailsClicked(track, holder.mMapView);
break;
case R.id.menu_tracklist_cardlayout_item_delete:
mTrackInteractionCallback.onDeleteTrackClicked(track);
break;
case R.id.menu_tracklist_cardlayout_item_export:
mTrackInteractionCallback.onExportTrackClicked(track);
break;
case R.id.menu_tracklist_cardlayout_item_upload:
mTrackInteractionCallback.onUploadTrackClicked(track);
break;
}
return false;
});
// Initialize the OnClickListener for the invisible button that is overlaid
// over the map view.
holder.mInvisMapButton.setOnClickListener(v -> {
LOG.info("Clicked on the map. Navigate to the details activity");
mTrackInteractionCallback.onTrackDetailsClicked(track, holder.mMapView);
});
}
/**
* Initializes the MapView, its base layers and settings.
*/
protected void initMapView(TrackCardViewHolder holder, Track track) {
// First, clear the overlays in the MapView.
holder.mMapView.getOverlays().clear();
// Set the openstreetmap tile layer as baselayer of the map.
WebSourceTileLayer layer = MapUtils.getOSMTileLayer();
holder.mMapView.setTileSource(layer);
// set the bounding box and min and max zoom level accordingly.
BoundingBox box = layer.getBoundingBox();
holder.mMapView.setDiskCacheEnabled(true);
holder.mMapView.setScrollableAreaLimit(box);
holder.mMapView.setMinZoomLevel(holder.mMapView.getTileProvider().getMinimumZoomLevel());
holder.mMapView.setMaxZoomLevel(holder.mMapView.getTileProvider().getMaximumZoomLevel());
// holder.mMapView.setCenter(holder.mMapView.getTileProvider().getCenterCoordinate());
holder.mMapView.setZoom(0);
if (track.getMeasurements().size() > 0) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
// Configure the line representation.
Paint linePaint = new Paint();
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setColor(Color.BLUE);
linePaint.setStrokeWidth(5);
TrackSpeedMapOverlay trackMapOverlay = new TrackSpeedMapOverlay(track);
trackMapOverlay.setPaint(linePaint);
final BoundingBox bbox = trackMapOverlay.getTrackBoundingBox();
final BoundingBox viewBbox = trackMapOverlay.getViewBoundingBox();
final BoundingBox scrollableLimit = trackMapOverlay.getScrollableLimitBox();
LOG.warn("trying to zoom to track bbox");
mMainThreadWorker.schedule(new Action0() {
@Override
public void call() {
holder.mMapView.getOverlays().add(trackMapOverlay);
LOG.warn("bbox " + bbox);
// Set the computed parameters on the main thread.
holder.mMapView.setScrollableAreaLimit(scrollableLimit);
LOG.warn("scrollable limit " + scrollableLimit.toString());
holder.mMapView.setConstraintRegionFit(true);
holder.mMapView.zoomToBoundingBox(viewBbox, true);
LOG.warn("zooming to " + viewBbox.toString());
}
});
return null;
}
}.execute();
}
}
/**
*
*/
static class TrackCardViewHolder extends RecyclerView.ViewHolder {
protected final View mItemView;
@InjectView(R.id.fragment_tracklist_cardlayout_toolbar)
protected Toolbar mToolbar;
@InjectView(R.id.fragment_tracklist_cardlayout_toolbar_title)
protected TextView mTitleTextView;
@InjectView(R.id.fragment_tracklist_cardlayout_content)
protected View mContentView;
@InjectView(R.id.track_details_attributes_header_distance)
protected TextView mDistance;
@InjectView(R.id.track_details_attributes_header_duration)
protected TextView mDuration;
@InjectView(R.id.fragment_tracklist_cardlayout_map)
protected MapView mMapView;
@InjectView(R.id.fragment_tracklist_cardlayout_invis_mapbutton)
protected ImageButton mInvisMapButton;
/**
* Constructor.
*
* @param itemView the card view of the
*/
public TrackCardViewHolder(View itemView) {
super(itemView);
this.mItemView = itemView;
ButterKnife.inject(this, itemView);
}
}
/**
* Default view holder for standard local and not uploaded tracks.
*/
static class LocalTrackCardViewHolder extends TrackCardViewHolder {
/**
* Constructor.
*
* @param itemView
*/
public LocalTrackCardViewHolder(View itemView) {
super(itemView);
}
}
/**
* Remote track view holder that only contains the views that can be filled with information
* of a remote track list. (i.e. users/{user}/tracks)
*/
static class RemoteTrackCardViewHolder extends TrackCardViewHolder {
@InjectView(R.id.fragment_tracklist_cardlayout_remote_progresscircle)
protected FABProgressCircle mProgressCircle;
@InjectView(R.id.fragment_tracklist_cardlayout_remote_downloadfab)
protected FloatingActionButton mDownloadButton;
@InjectView(R.id.fragment_tracklist_cardlayout_downloading_notification)
protected TextView mDownloadNotification;
/**
* Constructor.
*
* @param itemView the card view of the
*/
public RemoteTrackCardViewHolder(View itemView) {
super(itemView);
}
}
}