/**
** Copyright (c) 2010 Ushahidi Inc
** All rights reserved
** Contact: team@ushahidi.com
** Website: http://www.ushahidi.com
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: http://www.gnu.org/licenses/lgpl.html.
**
**
** If you have questions regarding the use of this file, please contact
** Ushahidi developers at team@ushahidi.com.
**
**/
package com.ushahidi.android.app.ui.tablet;
import java.util.List;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.TextView;
import com.actionbarsherlock.view.MenuItem;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.ushahidi.android.app.Preferences;
import com.ushahidi.android.app.R;
import com.ushahidi.android.app.adapters.CategorySpinnerAdater;
import com.ushahidi.android.app.adapters.ListFetchedReportAdapter;
import com.ushahidi.android.app.adapters.PopupAdapter;
import com.ushahidi.android.app.api.CategoriesApi;
import com.ushahidi.android.app.api.ReportsApi;
import com.ushahidi.android.app.entities.PhotoEntity;
import com.ushahidi.android.app.entities.ReportEntity;
import com.ushahidi.android.app.fragments.BaseMapFragment;
import com.ushahidi.android.app.models.ListPhotoModel;
import com.ushahidi.android.app.models.ListReportModel;
import com.ushahidi.android.app.tasks.ProgressTask;
import com.ushahidi.android.app.ui.phone.AddReportActivity;
import com.ushahidi.android.app.ui.phone.ViewReportSlideActivity;
import com.ushahidi.android.app.util.ImageManager;
import com.ushahidi.android.app.util.Util;
public class MapFragment extends BaseMapFragment implements
OnInfoWindowClickListener {
private ListReportModel mListReportModel;
private List<ReportEntity> mReportModel;
private Handler mHandler;
private int filterCategory = 0;
private MenuItem refresh;
private CategorySpinnerAdater spinnerArrayAdapter;
private boolean refreshState = false;
private UpdatableMarker mMarker = createUpdatableMarker();
public MapFragment() {
super(R.menu.map_report);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mListReportModel = new ListReportModel();
mListReportModel.load();
mReportModel = mListReportModel.getReports();
showCategories();
mHandler = new Handler();
if (checkForGMap()) {
map = getMap();
Preferences.loadSettings(getActivity());
initMap();
map.setInfoWindowAdapter(new PopupAdapter(
getLayoutInflater(savedInstanceState)));
map.setOnInfoWindowClickListener(this);
}
}
private void initMap() {
// set up the map tile use
Util.setMapTile(getActivity(), map);
if (mReportModel.size() > 0) {
setupMapCenter();
mHandler.post(mMarkersOnMap);
} else {
toastLong(R.string.no_reports);
}
}
@Override
public void onInfoWindowClick(Marker marker) {
if (mReportModel != null) {
List<String> markers = mMarker.markersHolder;
// FIX ME: Using the title to find which latlng have been tapped.
// This ugly hack has to do with the limitation in Google maps api
// for android. There is a
// posibility of having the wront position returned in case there
// are two or more of the same title.
// SEE:https://code.google.com/p/gmaps-api-issues/issues/detail?id=4650
final int position = markers.indexOf(marker.getTitle());
if (markers != null && markers.size() > 0) {
launchViewReport(position, "");
}
}
if (marker.isInfoWindowShown())
marker.hideInfoWindow();
}
private void launchViewReport(int position, final String filterCategory) {
Intent i = new Intent(getActivity(), ViewReportSlideActivity.class);
i.putExtra("id", position);
if (filterCategory != null
&& !filterCategory.equalsIgnoreCase(getActivity().getString(
R.string.all_categories))) {
i.putExtra("category", filterCategory);
} else {
i.putExtra("category", "");
}
getActivity().startActivityForResult(i, 0);
getActivity().overridePendingTransition(R.anim.home_enter,
R.anim.home_exit);
}
protected void setupMapCenter() {
if (map != null) {
final View mapView = getView();
if (mapView != null) {
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
// We use the new method when supported
@SuppressLint("NewApi")
// We check which build version we are using.
@Override
public void onGlobalLayout() {
LatLng latLng = getReportLatLng();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver()
.removeGlobalOnLayoutListener(
this);
} else {
mapView.getViewTreeObserver()
.removeOnGlobalLayoutListener(
this);
}
if (latLng != null)
map.moveCamera(CameraUpdateFactory
.newLatLng(latLng));
}
});
}
}
}
}
private LatLng getReportLatLng() {
if (mReportModel != null) {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (ReportEntity reportEntity : mReportModel) {
double latitude = 0.0;
double longitude = 0.0;
try {
latitude = Double.valueOf(reportEntity.getIncident()
.getLatitude());
} catch (NumberFormatException e) {
latitude = 0.0;
}
try {
longitude = Double.valueOf(reportEntity.getIncident()
.getLongitude());
} catch (NumberFormatException e) {
longitude = 0.0;
}
builder.include(new LatLng(latitude, longitude));
}
return Util.getCenter(builder.build());
}
return null;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.menu_refresh) {
refresh = item;
new RefreshReports(getActivity()).execute((String) null);
return true;
} else if (item.getItemId() == R.id.menu_add) {
launchAddReport();
return true;
} else if (item.getItemId() == R.id.menu_normal) {
if (Preferences.mapTiles.equals("google")) {
map.setMapType(GoogleMap.MAP_TYPE_NONE);
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
}
return true;
} else if (item.getItemId() == R.id.menu_satellite) {
if (Preferences.mapTiles.equals("google")) {
map.setMapType(GoogleMap.MAP_TYPE_NONE);
map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
}
return true;
} else if (item.getItemId() == R.id.filter_by) {
showDropDownNav();
return true;
}
return super.onOptionsItemSelected(item);
}
protected View headerView() {
LayoutInflater inflater = getActivity().getLayoutInflater();
ViewGroup viewGroup = (ViewGroup) inflater.inflate(
R.layout.map_view_header, null, false);
TextView textView = (TextView) viewGroup.findViewById(R.id.map_header);
textView.setText(R.string.all_categories);
return viewGroup;
}
// FIXME:: look into how to put this in it own class
private void showDropDownNav() {
showCategories();
new AlertDialog.Builder(getActivity())
.setTitle(getString(R.string.prompt_mesg))
.setAdapter(spinnerArrayAdapter,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
filterCategory = spinnerArrayAdapter.getTag(
which).getCategoryId();
final String all = spinnerArrayAdapter.getTag(
which).getCategoryTitle();
if ((all != null)
&& (!TextUtils.isEmpty(all))
&& (all != getString(R.string.all_categories))) {
mHandler.post(fetchReportListByCategory);
} else {
mHandler.post(fetchReportList);
}
dialog.dismiss();
}
}).create().show();
}
public void showCategories() {
spinnerArrayAdapter = new CategorySpinnerAdater(getActivity());
spinnerArrayAdapter.refresh();
}
/**
* refresh by category id
*/
final Runnable fetchReportListByCategory = new Runnable() {
public void run() {
try {
final boolean loaded = mListReportModel
.loadReportByCategory(filterCategory);
if (loaded) {
mReportModel = mListReportModel.getReports();
log("Filter reports by category: "+mReportModel.size());
populateMap();
}
} catch (Exception e) {
return;
}
}
};
/**
* Refresh the list view with new items
*/
final Runnable fetchReportList = new Runnable() {
public void run() {
try {
mListReportModel.load();
mReportModel = mListReportModel.getReports();
populateMap();
showCategories();
} catch (Exception e) {
return;
}
}
};
private void updateRefreshStatus() {
if (refresh != null) {
if (refreshState)
refresh.setActionView(R.layout.indeterminate_progress_action);
else
refresh.setActionView(null);
}
}
/**
* Restart the receiving, when we are back on line.
*/
@Override
public void onResume() {
super.onResume();
initMap();
}
public void onDestroy() {
super.onDestroy();
if (new RefreshReports(getActivity()).cancel(true)) {
refreshState = false;
updateRefreshStatus();
}
}
// put this stuff in a seperate thread
final Runnable mMarkersOnMap = new Runnable() {
public void run() {
populateMap();
}
};
/**
* add marker to the map
*/
public void populateMap() {
if (mReportModel != null) {
mMarker.clearMapMarkers();
for (ReportEntity reportEntity : mReportModel) {
double latitude = 0.0;
double longitude = 0.0;
try {
latitude = Double.valueOf(reportEntity.getIncident()
.getLatitude());
} catch (NumberFormatException e) {
latitude = 0.0;
}
try {
longitude = Double.valueOf(reportEntity.getIncident()
.getLongitude());
} catch (NumberFormatException e) {
longitude = 0.0;
}
final String description = Util.limitString(reportEntity
.getIncident().getDescription(), 30);
mMarker.addMarkerWithIcon(map, latitude, longitude,
reportEntity.getIncident().getTitle(), description,
reportEntity.getThumbnail());
}
}
}
public void launchAddReport() {
Intent i = new Intent(getActivity(), AddReportActivity.class);
i.putExtra("id", 0);
startActivityForResult(i, 2);
getActivity().overridePendingTransition(R.anim.home_enter,
R.anim.home_exit);
}
private void deleteFetchedReport() {
final List<ReportEntity> items = new ListFetchedReportAdapter(
getActivity()).fetchedReports();
for (ReportEntity report : items) {
if (new ListReportModel().deleteAllFetchedReport(report
.getIncident().getId())) {
final List<PhotoEntity> photos = new ListPhotoModel()
.getPhotosByReportId(report.getIncident().getId());
for (PhotoEntity photo : photos) {
ImageManager.deletePendingPhoto(getActivity(),
"/" + photo.getPhoto());
}
}
}
}
/**
* Refresh for new reports
*/
class RefreshReports extends ProgressTask {
protected Integer status = 4; // there is no internet
public RefreshReports(Activity activity) {
super(activity, R.string.loading_);
// pass custom loading message to super call
refreshState = true;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog.cancel();
refreshState = true;
updateRefreshStatus();
}
@Override
protected Boolean doInBackground(String... strings) {
try {
// check if there is internet
if (Util.isConnected(getActivity())) {
// delete everything before updating with a new one
deleteFetchedReport();
// fetch categories -- assuming everything will go just
// right!
new CategoriesApi().getCategoriesList();
status = new ReportsApi().saveReports(getActivity()) ? 0
: 99;
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
log("fetching ");
if (status == 4) {
toastLong(R.string.internet_connection);
} else if (status == 110) {
toastLong(R.string.connection_timeout);
} else if (status == 100) {
toastLong(R.string.could_not_fetch_reports);
} else if (status == 0) {
log("successfully fetched");
mReportModel = mListReportModel.getReports();
populateMap();
showCategories();
}
}
refreshState = false;
updateRefreshStatus();
}
}
}